カテゴリー:CSSテクニック

リスト表示へ

スマホでも動く物理演算で転がる3Dサイコロを作ったのでその解説

2013年9月 1日 16:46

CSS3DRendererはexamplesの中

ルービックキューブの解説は少しお休みして、Three.jsのCSS3DRendererを使ってサイコロを作った話をしようと思います。といっても、ルービックキューブを作る上で使用した概念などもあるので、まったくの無関係というわけではありません。

今回のポイントは「CSS3DRenderer」です。
これ、実はThree.jsだけを読み込んでも使用できません。実験段階だからなのか、Three.jsのgithubのリポジトリ内にある「examples」の中にこっそり含まれています。

実装サンプル

なので、まずはこれを取り出してThree.jsと一緒に読み込ませます。
ちなみに今回作成したものは以下のようなサイコロです。(CANNON.jsという軽量の物理エンジンも使用しています)

見てもらうと分かりますが、こうした基本的な形状であればCSSでも問題なく表示することが出来ます。
また、Three.jsでオブジェクトを生成したりテクスチャを貼ったり、という処理すらなく、単純にDOMを生成してそれを立体に仕上げるだけです。
つまり、このデモはiPhoneでも問題なく見れます。

立体にするための回転や移動のマトリクス的な処理は全部Three.jsがやってくれるので、直感的に形を作っていくことができます。(例:dice.rotation.x = THREE.Math.degToRad(45))

コード解説

ポイントを絞って今回のコードの解説をしていこうと思います。
まずはなにはともあれ、CSSで表示できるようにするまでを見てみます。

ソースコード

各面の情報の定義

順を追って見ていきます。まず最初はサイコロの各面用の情報を用意しています。position、rotation、テクスチャURLの3つです。
cubeSizeはキューブのサイズを別の場所で定義しています。サイコロのサイズですね。
cubeSizeは位置情報として利用しているので、実際のサイズは2倍にする必要があります。(中心点からの移動量なので、左にcubeSize、右にcubeSize分移動します。そのため、各面のサイズは2倍になります)

空のCSS3DObjectをdiceの親として定義

そして情報の定義の下に見慣れた記述があります。そう、まずは普通にdiv要素を作って定義されたキューブのサイズにサイズを指定します。
このdivがサイコロの各面を内包する要素となります。
生成したあとはTHREE.CSS3DObjectに、先ほど生成したdiv要素を指定してインスタンスを作ります(関連付けます)。
こうすることでThree.jsの3Dの変換などがこのdiv要素に適用されるようになる、というわけです。

各面を生成

最後に、サイコロの各面を生成します。boxInfoに6面分の情報が入っているのでそれをfor文で回し、各面を生成します。
生成の仕方はdiv要素のときとまったく同じです。DOMを生成し、それをTHREE.CSS3DObjectに関連付けるだけです。
生成後、boxInfoで定義された位置と回転情報を適用します。(face.position.fromArray(info.position)) そして生成された各面をdiceに追加します。(dice.add(face);

サイコロの生成はこれだけです。
あとはここで生成したdiceをThree.jsのsceneオブジェクトにaddし、rendererでレンダリングしてあげればサイコロが出現します。
Three.jsの使い方はここでは長くなってしまうので割愛します。使い方については「THREE.js で WebGL>」に詳しく書かれているのでこちらを参考にするといいと思います。
SceneやCamera、Rendererの生成の仕方はまったく一緒です。(ただし、今回の目的でもあるCSSでレンダリングさせるために使用するレンダラーは「CSS3DRenderer」を利用する必要があります)

サイコロのどの面が上かを判定する

実はここが一番書きたくてこの記事を書いていますw
今回はCANNON.jsというのを使って物理演算させていますが、これの使い方についてはまた別の機会にしたいと思います。(まだそこまでちゃんと使ってないし)

さて、どの面が上を向いているかどう判別したらいいでしょうか。人間であれば見たまま一目瞭然ですね。
しかし、コンピュータはそれを数値で表さないとなりません。なので、なんらかの計算をした上で、その数値から判断する必要があります。

では、どうしたらそれを判別できるか。まずは「なにがどうなっていたらそれは上を向いていると見なせるか」を考えてみます。
下の画像は、サイコロと座標系の軸を表示したものです。(最近ハマっているので、無駄にBlenderで作りましたw)
サイコロを置いて、XYZ軸の向きを表示したもの。Y軸正の向きに1、X軸正の向きに3が向いている
画像を見ると「1」が上を向いていますね。そして軸の情報を見てみると、「Y軸の正の向き」が上向きと一致しています。

では次の画像はどうでしょうか。今度はサイコロが回転して、X軸の正の向きが上を向いています。
上記画像のサイコロが回転し、今度は3とX軸正の向きが画面上を向いている状態
どうやらどの軸のどの向きが上を向いているか、が分かれば上を向いている面が判別できそうです。
しかし、サイコロはCANNON.jsの物理演算によってぐるぐるとたくさん回転してしまっています。実際それが今どうなっているかはどうしたら分かるでしょうか。

Three.jsはワールド空間座標での変換行列を持っている

実はThree.jsのCSS3DObjectは(もちろん、WebGLとして作る場合のObject3Dオブジェクトも)、このワールド空間座標でどの位置にどんな向きでモデルが配置されているか、という情報を持っています。
それはmatrixWorldというプロパティに保存されています。
この変換行列を適用すると、とある点がワールド空間上でどの位置にあるか、が分かります。
つまり、この行列を使えば現時点のワールド空間座標系で軸や頂点がどういう向きになっているかが計算できるというわけです。

X軸正の向きで試してみる

実際にやってみましょう。試しにX軸正の向きがどちらを向いているかやってみます。
jsdo.itに上がっているサンプルで、「2」が上向きの状態で試してみます。

まずX軸の正の向きを表すベクトルを作ります。ベクトルを作るには、今回はTHREE.Vector4クラスを使います。THREE.Vector4クラスにはapplyMatrix4というメソッドがあり、自身のベクトルに行列を適用することができるようになっています。
実際にベクトルを生成している部分は以下です。

このベクトルに対して行列を適用すると結果は「x: -0.00044029735727235675, y: -0.9999998807907104, z: 0.00009558126475894824, w: 0」となりました。この値が意味するところはなんでしょうか。
これは、行列適用後X軸正の向きがワールド空間座標系でどの方向を向いているか、というベクトル情報です。

本来なら「1」が向いている方向となりますが、ものすごーくわずかに真上からずれた方向を向いているようです。

実はプログラムで数値を計算する以上、どうしても限界があります。細かい数値はある程度切り捨てられます。無限の桁数では計算できませんからね。
なので、数回の計算を行ううちに、この切り捨てられた数値部分の誤差がどうしても出てしまいます。
それが、この「-0.9999998807907104」という数値になって現れたのです。

とはいえ、他の数値に目を向けてみると明らかに値が違います。ここは誤差を許容して0.99以上、とすることで判別ができそうです。

その判定を行っているのが「if (px.applyMatrix4(dice.matrixWorld).y > UP) {...}」の部分です。(UPは0.99を変数に入れているだけです)
この「y」は上で説明した通り、計算後のX軸正の向きの現状の向きです。つまり、yの値が-0.99ということはマイナス値、すなわち下を向いている、ということになります。

すべての軸・方向についてチェックする

これを、各軸各方向すべてに対して確認することで、どの面が上をむいているかを判別しています。
実際のソースは以下です。

実際のソースを見てみると、各軸の各方向について計6種類のベクトルを生成し、それに行列を適用させてその結果の判定を行っています。
もしyの値が0.99以上の値を持つものが見つかったら、それを面の上の結果として判断しその面に描かれているサイコロの目を出力する、という流れです。
どの軸の方向がどの数字か、は作ったサイコロによるのでそこは事前に決めておかないとなりません。
そこがしっかりと定義できていれば、あとは判別した軸と合わせて該当する数字を使うだけです。

まとめ

いかがだったでしょうか。
実際にスマホで見てみても、PCとまったく遜色なく動くと思います。(特にiPhoneは)
CSSで問題なく作れる形状に関してはスマホでも動くものが比較的簡単に作れます。
CSSということは表示しているものはDOMそのものなので、画像を表示するもコンテンツを表示するも自由自在です。

CSS3DRendererはだいぶ夢があるなぁと思いました。(まぁ、iPhoneでWebGLが動いてくれるようになれば話しは早いんですけどね・・)
コンテンツにちょっとした驚きを追加するにはとてもいい選択だと思うので、ぜひ色々試してみてください。

さて、次回はまたルービックキューブの話に戻ろうと思います。(ちなみに今回のこの軸の方向がどちらを向いているか、というのはルービックキューブの制作でも使用しました)

CSSで本当にクリアできるルービックキューブを作ったので3Dについて色々解説してみる その2

2013年8月28日 19:04

前回はこれから解説していくルービックキューブの紹介と、3D関連の学習に利用した記事などの紹介をしました。
いよいよ実際にルービックキューブについて解説をしていきたいと思います。
まずは基本的なところから始めて行くので、人によっては今回の記事はほぼ知ってることだらけになるかもしれません。

要素の変形 - transformプロパティの基礎

上でも書いた通り、まずはtransformプロパティの基礎から解説していきます。
すでに使ったことがある人ならおなじみだと思いますが、transformプロパティ(WebKit/Blink系はまだ-webkit-のプレフィクスが必要です)にはいくつかの値が指定できます。

実際にtransformプロパティに指定できる関数をいくつか適用したサンプルをjsdo.itにアップしておきました。
以下のサンプルを見てもらうとそれぞれがどういう動きをするか分かると思います。

translate関数

translate関数は平行移動をさせる関数です。translate(30px, 40px);と指定すれば、その要素は右に30px、下に40px移動します。
ただし、marginと違いposition: relative; left: 30px; top: 40px;の指定とほぼ同じ動作をします。つまり、移動結果で他の要素に影響を与えることがありません。

rotate関数

rotate関数は回転をさせる関数です。rotate(30deg);と指定すれば、その要素は時計回りに30度(degrees)回転します。
translate関数同様、移動後の結果は他の要素に影響を与えません。

scale関数

scale関数は拡大・縮小をさせる関数です。scale(2, 2);と指定すれば、その要素は縦横ともに2倍に拡大されます。
仮にscale(1, 2);とすれば、縦だけが2倍に拡大されます。
scale関数も他と同様、結果は他要素に影響を与えません。

skew関数

skew関数はせん断する関数です。skew(30deg);と指定すれば、その要素は30度分、せん断されます。実際にどうなるかは上記のサンプルを見てください。
skew関数も結果は他要素に影響を与えません。

関数まとめ

これら関数は単独で使う以外にも、組み合わせて使うこともできます。 回転させた後に拡大、みたいなことがやりたい場合はtrnasform: rotate(30deg) scale(2);とすることでふたつを組み合わせた変形をすることができます。
その他、関数を任意の数指定することでそれぞれの変形を重ねあわせることもできます。

ただしひとつ注意点があります。それは、関数の組み合わせる順番に意味がある、ということです。
詳しくはこの先の行列あたりの話でする予定ですが、移動してから回転させるのか、回転してから移動させるのかで当然結果が変わってきます。
なぜかと言うと、例えばX軸方向に移動させたい場合、rotate関数はX軸自体も回転させてしまうからです。つまり、回転したあとのX軸方向に移動してしまうため結果が変わってしまうのです。
実際のサンプルは以下です。
rotate x translatetranslate x rotateの違いを表しています。

回転の角度は同じで若干分かりづらいので、元の位置を点線で表しています。元の位置からの違いを確かめてみてください。

実は関数は2D用と3D用の2種類

さて、すでにお気づきの人もいるかと思いますが、上で説明していたのは実は2Dに対しての変形でした。
だって、パラメータをふたつしか渡していないですからね。(e.g. translate(30px, 40px))

3Dは3軸に対しての変換なので3つのパラメータが必要です。(XYZ軸ですね)
ただし、上記の関数に単純に3つのパラメータを渡してもダメです。3D用には専用の関数があります。
といっても大した違いはなく、上で説明した関数名の最後に3dをつけるだけです。
つまりtranslate3d(10px, 10px, 10px)という具合です。(この例みたいに3つの値すべてが同じ場合は引数ひとつでも同じ動作になります)

3Dの変換にはパースが必要

さて、実際に3dをつけて変換を行ってみるとおかしなことに気づくと思います。どういうことかと言うと、3Dっぽくない。
なぜ3Dっぽくないのか。理由は、パース(遠近感)がついていないから、です。
遠近感は人が目にするものは(現実世界であれば)すべてに必ずついているものです。遠くのものほど小さく見える、というあれです。

しかし、コンピュータの世界はそれらもしっかりと数値で指定してあげないと解釈ができません。つまり、3Dの指定だけではダメで、ちゃんとパースをどれくらいつけるか、という指定も同時に必要になります。(注意点として、パースの指定は回転を指定した要素の親要素に指定する必要があります)

実際にその違いを以下のサンプルに用意してみました。実際にどれくらい違いがあるのか見てみてください。

並べてみてみるとその違いは一目瞭然ですね。下のほうがより自然に回転しているように見えます。
今回はパースの値を300pxで設定しましたが、この値をいじることで少しまた違った見え方になるので色々いじってみてください。

変換の要、マトリクス

さて、今回はまだ続きます。でもこれが最後です。そして難関です。
最後はマトリクスです。または行列と言います。

マトリクスのむずかしい点は、まずその引数の多さでしょう。2D用のものでも引数は9個、3D用に至っては16個もの数値を指定しないとなりません。
さらに、それぞれの数値は分かりやすい数値にはなっていません。例えば、X軸に対して30度回転させたい、となった場合でも、どこにも30という数字は出てきません。

実際に指定するとmatrix3d(1, 0, 0, 0, 0, 0.8660254, 0.4999999, 0, 0, -0.4999999, 0.8660254, 0, 0, 0, 0, 1)という数値になります。30なんて数字はどこにも出てきていませんね。
なぜこんな複雑な数値を指定する必要があるのでしょうか。それは、これが回転や拡大・縮小、平行移動などの処理をひとまとめにしたものだからです。(上のサンプルは回転ひとつだけですが、いくつも回転や平行移動などを掛けあわせても、指定するのはひとつのマトリクスだけになります)
実際、今回作成したルービックキューブでは、今キューブのそれぞれの面はどこを向いているのか、というのをこのマトリクスを使って表現しました。なぜなら、ユーザの操作によってキューブは常に姿勢が変わってしまいます。その都度、rotateXがいくつで、rotateYがいくつで・・などとやっていたら、style属性に指定する関数があっという間にとんでもない数になってしまいます。

しかし、このマトリクスを使うことで、何回回転されたとしてもひとつのマトリクスでそれを表現することができます。これが、ひとつにまとめられるメリットです。
行列に関しては別で説明しますが、こうした理由からマトリクスを使って姿勢制御をするという選択を取りました。

実際に指定したものは以下になります。
matrix3dに指定する数値の桁を若干省略したので微妙に角度が違いますが、しっかりと数値を指定すれば同じ見た目になります。

まとめ

いかがだったでしょうか。だいぶ基本的なところなので、知っている人が多かったかと思います。
ただ、マトリクスは普段あまり使わないのではないでしょうか。ルービックキューブでは逆にこれを多用しているので、使ったことない人はぜひ、使い方を知って活用してみてください。

だいぶ長くなりましたが、transformについてまとめました。次回は座標変換や行列について書きたいと思います。(CSSまったく関係なくなっちゃいますが・・)

CSSで本当にクリアできるルービックキューブを作ったので3Dについて色々解説してみる その1

2013年8月20日 09:04

しばらくぶりの更新です。
最近はWebGLやCSS Shaderを始め、3D関係のことをメインに勉強しています。
おかげでだいぶ詳しくなってきました。

ちなみに、CSSでクリアできる、と書いていますが「CSSだけで」ではないので当然JavaScriptもてんこ盛りですw

最近はインプットばかりだったので、備忘録も兼ねて以前案件で制作したCSS Rubik Cubeを解説を交えながら書いていきたいと思います。
CSS Rubik CubeはMaxellの商品のキャンペーンで、エビ中キューブと名付けられたコンテンツです。

エビ中キューブは、エビ中というアイドルグループの動画を、ルービックキューブで操作して自分好みのものに揃えて見る、というものです。
PCだけでも操作可能ですが、これをWebSocketでスマホと接続してスマホからPCに表示されているルービックキューブを操作する、というのがメインコンテンツです。
この「スマホでルービックキューブ」のコントローラ部分の制作を担当しました。

今回はこの「スマホ向けルービックキューブコントローラ」を細かく解説していこうと思っています。(ただ、結構長くなりそうなので何回かに分けて書く予定です)
ちなみにこのコントローラ部分をjsdo.itにあげて、さらに何分でクリアできるか、というゲーム仕立てにしたものをアップしておきました。

以下のデモは、マウスでドラッグするか、スマホでフリップすることでルービックキューブを操作することができます。画面外をドラッグすることでルービックキューブ自体を回転することができるので、全体の状態がどうなっているかも確認することができます。(ただ、これでクリアは相当大変ですが・・w)

実際のルービックキューブサンプル

3Dに関するメモ書き

3Dグラフィクスに関してはインターネット上の記事をメインに、書籍などを参考にしつつ勉強しました。
そのときに集めたメモ書きをqiitaに投稿しているので、そちらも参考にしてみてください。

自作3Dライブラリのデモ

勉強の理解を深めるために、1から自作の3Dライブラリを作ってみたものが以下のデモです。
座標変換、ライティングがメインなので複雑なモデルは作れていませんが、ドラッグなどで3Dの感じを味わえる作りになっています。
またデモは、2D Canvasにレンダリングするものなので、(かなり重いですが)スマホでも見ることができます。

参考書籍

書籍は以下のもの(実例で学ぶゲーム3D数学)を熟読しました。

参考記事リンク

自分が勉強してきた記事で、ブックマークしたものをまとめておきました。
ただひとつ注意点があって、ググって出てくる記事には2通りの種類があるということを覚えておいてください。

どういうことかというと、OpenGLをベースに解説している記事なのか、DirectXをベースにしている記事なのか、です。
次回以降で詳しく解説する予定ですが、このふたつはどちらも3Dを実現する技術です。
なにを注意するかというと、実はこのふたつ、どちらも同じ3D関係の技術ではあるものの、3Dには欠かせない行列による計算がまったく逆なのです。

そのため、これを知らずに行列を計算すると期待と違った結果になってしまいます。
色々な記事を参考にしていて、実はここが地味に大変でした。この行列はどっちのなの!? というw
なので、ここに挙げた記事以外に他のものを参考にする際はその点に注意して参考にしてください。(ちなみに解説する予定の行列の計算はOpenGLの方式をベースにしています)

今回は参考リンクや触り部分だけになりましたが、意外と参考記事を見つけるだけでも大変だったので有用だと思うものをまとめておきました。
次回はいよいよ本格的に解説に入って行きたいと思います。

CSS Shaderで遊ぶ(2)

2013年5月26日 08:15

前回記事、「CSS Shaderで遊ぶ」ではCSS Shaderの触りを書きました。
詳細にはほとんど触れていませんが、どんなことができるのか、CSS Shaderの可能性は感じてもらえたのではないでしょうか。

今回の記事では、前回触れたUIを作るという点にフォーカスを当てて、簡単なエフェクトを作ってみるとともに、シェーダでどんなことをやっているのかひとつずつ見ていこうと思います。とはいえ、自分もまだまだ勉強中の分野のため、間違いだったり勘違いがあるかもしれません。なので、読む方はそれを念頭に読んでいただければと思います。

CSS ShaderでUIを作る

ということで、さっそく今回のために作成したサンプルを見てみようと思います。サンプルはまたしてもjsdo.itに上げました。
UIということで、分かりやすく、Mac OSXのジニーエフェクトを模したものを作りました。
サンプルを見てもらうと分かりますが、実際書いているコードはそれほど多くありません。(JS部分はだいぶ荒削りになってますが)
(ちなみに、3つ並んでいるボタンの中央、「-」ボタンを押すことでエフェクトが実行されます。また、Shiftキーを押しながらクリックするとゆっくりエフェクトします)

CSS Shader適用の流れ

CSS Shaderでは以下の様な流れで状態を決定します。filterの指定の仕方は前回の記事を参照してください。

  • 適用した要素を、指定されたメッシュ数で分割する
  • 分割した部分のそれぞれの頂点位置情報を頂点シェーダに送る
  • 頂点シェーダ内で、CSSから渡された頂点位置情報を元に変形のための計算を行う
  • 頂点シェーダ内で計算された値を(必要であれば)フラグメントシェーダに送る
  • フラグメントシェーダで各ピクセルに対して計算を行い、最終的なピクセルの色を決定する

やや複雑ですが、大雑把に言うとこんな感じの流れになります。
前回の記事で掲載した画像が以下です。上記の処理を図にしたものです。

頂点シェーダに送られる位置情報は、上記画像で言うとcustom()と書かれた範囲の一番最初の、細切れにされたそれぞれの点の位置になります。

次に処理されるフラグメントシェーダはというと、上記の頂点情報から計算され変形された形をピクセル(フラグメント)に分割された各ピクセルに対して計算することになります。

頂点から次の頂点までの間のピクセルに関しては、頂点で計算された値は適切に補間されて値が渡されます。
つまり、頂点シェーダでの計算よりフラグメントシェーダでの計算のほうが回数が多く、単純に高負荷となります。

シェーダソース(Vertex shader)

さて、さっそくソースを見ていきます。今回は頂点シェーダ(Vertex shader)のみです。
フラグメントシェーダでは入力された値を、そのまま出力します(つまりなにもしない)。
まずはソース全文を載せ、続いて個別に解説していきます。

ソース解説

3Dのレンダリングは非常に細かい計算が必要です。そのため頂点シェーダでは基本、最高の精度で計算されます。
しかし当然ながらその分、計算の負荷が高くなります。今回のサンプルではそれを中精度で計算するよう宣言しています。(ただ、精度を指定しなくてもデフォルトが決まっているので問題はありません)
精度は3種類、lowp、mediump、highpが指定できます。

attribute変数

続いてattribute変数です。
attribute変数は頂点ごとに異なる値がCSSから渡される変数です。異なる値と言えば、頂点の位置情報がありますね。
位置以外にも色々情報があるので、頂点属性(attribute)と覚えておくといいでしょう。

前回記事で説明したように、指定されたDOM要素を複数のメッシュで分割し、それぞれの頂点位置を頂点シェーダに送ることで頂点位置を計算することができます。
頂点位置情報はa_position変数に自動で渡されます。つまり、頂点位置はすべて違った値ということになります。

また、頂点以外にも同じような値としてattribute変数に値が渡ってきます。今回の例で言えばa_texCoordです。
これは上記の頂点の位置情報と似たような値ですが、指定されたDOMをさもひとつのテクスチャ画像と見なし、現在計算中の頂点の位置にあるテクスチャ座標を渡してくれる変数です。

ただ注意点としては、a_positionで渡される値が[-0.5, 0.5]の間であるのに対し、a_texCoordは[0.0, 1.0]の間の値が渡されることです。今回のサンプルではこの値を角度に見立てて、下部に行くほど頂点位置が中央に寄っていく演出に利用しました。

a_position,a_texCoordはCSSから自動的に渡されるので、宣言をしておくだけで大丈夫です。(変数名は決まっているのでサンプルと同じように書いてください)

uniform変数

uniform変数はattribute変数と違い、全体で共通の値として設定される値です。
今回の例で言えばu_projectionMatrix,u_timeがそれに当たります。どちらも各頂点で同じ値が使用されます。

u_projectionMatrixは、透視投影(プロジェクション)変換行列です。
行列の意味は長くなるので割愛します。この値も同様にCSSから自動的に渡されるので、宣言をしておくだけで大丈夫です。

u_timeはサンプルで使用するために宣言している変数です。u_projectionMatrixなどと違い、制作者が任意に宣言、使用することができます。
今回の例では、「-」ボタンクリック後からの時間を計測し、それに伴って下部が徐々に縮小していく演出に用いています。

CSSが自動的に設定する変数と意味についてはQiitaの記事でまとめているのでそちらを参考にしてください。

const変数

constは他のプログラムと同様、定数を宣言する部分です。今回は円周率の値として定義しています。

main関数

main関数です。GLSLはC言語ライクな言語なので、main関数を必要とし、各頂点ごとにmain関数で記述した内容が実行されます。

細かい処理についてはコメントに詳細を記載しています。最初に書いたように、a_texCoord.yを角度に見立てて、頂点位置が下に移動すればするだけ中央に寄る計算を行なっています。

最後のgl_Position = u_projectionMatrix * pos;の部分は、計算した位置情報に、プロジェクション変換行列を掛けたものをgl_Positionという特殊変数に渡しています。
これはGLSLの仕様で、gl_Positionに位置情報を渡すことで頂点位置をプログラム側(WebGLやCSS Shaderなど)に伝えることができます。

まとめ

CSS Shaderは、CSSと名がついていながら、実際にはプログラムを記述する必要があります。しかし覚えれば表現できる幅が格段に広がるのでがんばってでも覚える価値はあるでしょう。

ですが、やはりプログラムはちょっと・・という人もいると思います。
幸いにしてシェーダ部分はふたつのファイルに分けて管理されるので、シェーダ部分はライブラリなどを利用し、ちょっとした変化や移動する度合いなどは上記で説明したuniform変数に渡すだけ、という使い方もできると思います。

個人的な意見としてですが、実装がもっと進めばそうしたライブラリがいずれ出てくると思うので、どう使ったらいいか、というのはぜひ覚えておくといいでしょう。

CSS Shaderで遊ぶ

2013年5月16日 09:25

久々の更新です。さらに、ものすごく久々にCSSのネタですw
今回はCSS Shaderについて触れたいと思います。
内容がだいぶ濃いので、徐々に調べながら何回かに分けて書いていこうと思っています。
CSS Shaderについて詳しく話すと膨大な量になる上に、そもそもそこまでまだ詳しくない(w)ので、今回はどんなことができるの? どうやるの? というのが分かる程度の記事にしたいと思います。

CSS Shaderとは

CSS Shaderとは、CSSでOpenGL ES2.0のGLSL(シェーダ記述言語)を利用し、DOMを様々な形に変形、演出することができる仕様です。(ちなみにOpenGLのGLは「Graphic Library」の略)

OpenGL ESはWebGLのシェーダ部分でも利用されているので、WebGLでなにか書いたことがある人はほぼそのまま使うことが出来ると思います。
ただ、CSSで利用するにはいくつか決まり事があり、以下の制約があります。
そのため、GLSLではCSSから渡された情報を元に変形や色の指定を行う必要があります。

  • 各頂点位置データはCSS側で準備し、a_position変数に渡される。
  • 位置データ以外の情報についても、いくつか決まった変数で渡される。(テクスチャ座標など。渡されるデータの種類についてはQiitaの記事でまとめています)
  • シェーダに渡したい情報がある場合は、custom function内に変数名 値(例) hoge 30.0)の形で指定します。

ちなみに、実際に適用してみた例が以下のものです。キャプチャ画像を見てもらうと分かりますが、ただのGoogle Mapを地球儀風に変形しています。
※現在はまだ試験的機能です。そのため、下記のサンプルは2013.05.15時点ではChromeのみ対応しており、かつchrome://flagsから設定を開き、CSS Shaderを有効にするのフラグをオンにしないと動作しません。試験機能をオンにする場合は自己責任でお願いします。

サンプルでは変形と一緒に、ライティング処理もしています。
CSS Shaderでは形状の変形とは別に、変形した内容に対して色を変化させることができるようになっています。
それを応用することで、まるで太陽の光を浴びているような地球の状況が再現できる、というわけです。

形状の変形には頂点シェーダ(Vertex shader)、色の変化にはフラグメントシェーダ(Fragment shader)をそれぞれ利用します。

Custom functionの使い方

CSS Shaderは、filterプロパティにcustomを指定することで実現することができます。

2013.05.15時点ではcustom filterに対応しているブラウザはChromeのみです。かつ、実際に指定する場合には-webkit-のプレフィクスが必要です。

シェーダに関してはまた別の機会に説明したいと思います。(まだ勉強中のため)
メッシュ分割数については、下記の画像のように、filterを適用した要素を指定された数字で縦横を分割します。
例で言えば32分割され、それぞれの点に対して頂点情報が設定され、それが頂点シェーダ(vertex shader)に送られます。
頂点シェーダでは、その渡された値を様々に計算して、最終的な頂点位置を決定します。

CSS Shaderはどこで使う?

上で示したサンプルなどを見ていて、どんなことができるか、というのはある程度分かったかと思います。
しかし、じゃあこれをどう使うの? という疑問があるかと思います。確かにぱっと見はすごいことができて、なんでもできそうですが、どう活かしていいか分かりませんよね。
元々、OpenGLはMac OSXなどのUIでも利用されているグラフィックライブラリです。MacのUIはとてもきれいで、アニメーションなんかも頻繁に使われていますよね。こうした表現にはGPUを直接利用できるOpenGLはとても有利です。

つまり、CSS Shaderが標準的になればWebサイト(あるいはWebアプリ)のUI部分はCSS Shaderを利用する、というシーンも増えていくのではないかなと思っています。
今現在ではむずかしい、Macのジニーエフェクトみたいなものも、CSS Shaderを使えばすんなりと実装できるでしょう。(使うことがいいかどうかは置いておいて)
CSS Shaderは表現の幅はもちろん、こうしたUIの新しい仕組みを定義してくれる素晴らしい仕様だと思っています。

これは簡単! a要素にクラスを追加するだけでiPhone UIボタン

2011年8月29日 09:05

↓実際に動いているデモはこちら(iPhone向けに作っているため、iPhone / WebKitでしか最適化されていません)

これは簡単! a要素ひとつでiPhone UIボタン - jsdo.it - share JavaScript, HTML5 and CSS

ちなみにiPhone / WebKitで実際に見るとこんな感じに見えます↓

実際に適用したサンプル

a要素に1クラス追加するだけで実現するのでお手軽

今回のサンプル作成でこだわったのは、1要素に1クラスを指定するだけでiPhoneのようなデザインを実装することです。
つまり、以下のように指定するだけでiPhone風ボタンに早変わりする、というような手軽さを意識しました。

HTMLソース

CSSソース

実は見出し部分も画像なし

実はタイトル周りもCSSのみで実装しています。
ソースもかなりシンプル。
さらには背景の縞々模様もCSSのみで作っていたりします。

まとめ

CSS3のポテンシャルは非常に高いです。
こうした比較的シンプルなデザインであればCSSのみで、かつ要素を何重にも入れ子に・・なんてしなくても実現できてしまいます。
(具体的には、:after擬似要素、:before擬似要素を匠に使うことで、無理に思えるデザインも実装できてしまったりします)
[追記] Twitterで指摘をもらいましたが、:after(:before)擬似要素はCSS3からの定義ではありません。ここで言いたかったのは、それらを工夫して使うことでCSS3の力が増し、CSSのみで複雑なデザインが実現できる、ということでした。:after(:before)擬似要素がCSS3だと勘違いされる人がいるかもしれないので追記しておきます。

iPhone4の対応で画像サイズがーという悩みを解消できることを考えると、色々とテンプレートとして作ってみるのも面白いのではないでしょうか。
ぜひぜひみなさんも色々なデザインを、少数構成で実現できるように挑戦してみてください。

ちなみにこのサンプルは、自分で作ったCSS3ジェネレーターでグラデーションを調整しながら作りました。よかったら使ってみてください。

CSS3なら簡単! アニメーションするリングメニューを作る

2011年7月19日 07:40

jsdo.itで公開したら意外と好評だったので、解説などを書いてみたいと思います。

ゲームなどにありそうなリングメニューを、CSSだけを使って実装した例です。下に実際の動くサンプルもあるので、ぜひ触って見てください。

ちなみに右の画像は、中央の「main」にマウスオーバーした際にアニメーションでメニューが出てくる瞬間の画像です。

デモ

まずは下のサンプルをご覧ください。ちなみにアニメーションに対応しているブラウザのみ、アニメーションが実行されます。具体的には、GoogleChrome、Safari、Firefox、Operaです。

リングメニューをCSSで実装してみる - jsdo.it - share JavaScript, HTML5 and CSS

テクニックの肝

今回のテクニックの肝は、transform-originです。このプロパティは、transform系の値を変更する際、どこを基点にするかというものになります。

このtransform-origin、デフォルト値は50% 50%となっています。つまりその要素の縦横中央、ということですね。もしこれを0 0とすれば左上が基点となり、100% 100%とすれば右下が基点となるわけです。そしてこの数値には、pxも指定することができるので、細かく位置を調整することも可能となります。

磨りガラスで見たような にじんだ文字を表現するtext-shadow

2011年2月 7日 09:00

text-shadowを使って にじんだ文字を表現する

前回と合わせて連続でtext-shadowネタです。今回は、text-shdaow のぼかし部分を利用して、CSS のみでテキストをぼかすテクニックの紹介です。

今回のテクニックは text-shadow に対応しているブラウザであれば、CSS のみで実現可能です。
まずは下の jsdo.it のサンプルを見てください。(下の方にある「Play」ボタンを押すと実行されます)
スタイルをtextareaに設定しているので、ぼかし状態の文字が入力されるのが確認できると思います。

[Demo] Blur text for CSS3 - jsdo.it - share JavaScript, HTML5 and CSS

ちょっとだけ解説

まずポイントとなるプロパティは当然、text-shadowです。
普通はテキストに影をつけるためのプロパティですね。
それを以下のようにすることでぼかし部分だけを表示することができます。

color が大事なポイントです。普通はカラーは色をつけるのが当たり前ですが、今回のテクニックの場合はこれを透明にしてしまいます。
つまり、影の上に乗っているテキストの色を透明にすることで、結果、影だけが表示されぼかし効果が残る、というわけです。

アニメーションを追加してより効果的に

サンプルを WebKit 系ブラウザで見ている人は気づいていると思いますが、実はこのサンプルにはアニメーションを設定しています。
text-shadow のぼかし部分を 0 から 10px などに指定することで、プレゼンツールで表示されるような効果を得ることができます。ぜひ試してみてください。

超立体的!text-shadowの意外な使い方

2011年2月 1日 09:32

text-shadowだけで手軽に立体文字を作る

元々は CSS3 Watch の記事じゃないか、というコメントを頂きました。調べたところ、そこでこのCSSに関する記事が見つかったので参考として記載しておきます。

今回は、とあるサイトで見かけた面白い表現を紹介したいと思います。
右にある画像が、その表現を拡大したものです。これはテキストに対してtext-shadowを指定しただけです。

もちろんテキストなのでコピペもできるし、textareaに指定すれば入力した文字がこのような立体的な文字になります。

下にjsdo.it上で作成したデモを載せてあります。下のほうにある「Play」ボタンを押すことでサンプルが実行されます。
サンプルはtextareaなので、自由にテキストを入力してみてください。(対応ブラウザは WebKit 系か、Firefoxです)

※ちなみにアンチエイリアスのかかったフォントじゃないとあんまりきれいに見えませんw

ちょっとだけ解説

今回のこのテクニックの肝はtext-shadow(というかそれだけ)です。

複数指定できるtext-shadow

この text-shadow、実は影の部分の指定を複数指定することができるのです。
意外と知らない人は知らない気がするこの指定、カンマ区切りで列挙すればひとつの要素に対して様々な影をつけることが可能になります。

さて、ではどんなことをやっているかというと、サンプルを見ていただくと一目瞭然ですがこの「影を複数指定できる」というのを利用して、立体的に見えるようにたくさんの影を指定することで実現しています。

.realTextクラスに指定している text-shadow の値の最初の 5 つが、文字が飛び出しているように見せる部分。そのあとの 7 つが、その盛り上がった文字からできる影を表現しています。

最初の 5 つの指定で、徐々に暗くなるように色を指定しています。(実際のものは同じ色でも光の当たり方によってグラデーションができます。それを表現している、というわけです)

そして次の 7 で、盛り上がった(ように見える)テキストから、さも影が落ちているような演出をしている、というわけです。

IE では・・・?

残念ながら、最初のほうにも書いた通り、対象ブラウザが限定されてしまいます。
しかし、対応していないブラウザでも、立体的に見えないだけでテキストは正常に表示されます。
なので、こうしたリッチ表現ができるブラウザにはリッチな表現を、そうでないブラウザに対しては情報にアクセスできるようにしておく、という対応ができます。いわゆる「プログレッシブ・エンハンスメント」ですね。

Firefoxでもtext-overflowを実現するテクニック

2010年6月 9日 09:39

text-overflowの実装状況

最初に、Firefox ではまだtext-overflowを実装していません。
もともとは IE の独自実装であり、現在は CSS3 で策定中の仕様となります。

ところが、Opera、webkit は先行実装しているので対応しています。
しかし、Firefox は前述したようにまだ実装されていません。
そこで、Firefox 向けにもなんとか text-overflow と同じ効果を得るテクニックの紹介です。

今回のテクニックの肝は、margin の使い方です。
とりあえず実際に動いているデモを見てください。(Firefox で見てください)

実際に動いてるサンプル

サンプルを見ると、しっかりとテキストか溢れたときだけ...が表示されているのが分かると思います。
(Firefox の【表示>ズーム>文字サイズだけ変更】のチェックを入れれば、拡大・縮小でテキストサイズだけが変更できるので、それでサイズを変更するとより分かりやすいと思います) text-overflow は他の主要なブラウザではすでに実装されているので、それらのブラウザ向けにはそのものを使い、Firefox 向けにだけ今回のテクニックを使っています。

ちなみに Firefox3.0 も対象とするものと、除外したものふたつを紹介します。
やっていることはほとんど同じですが、3.0 では実装の違いからうまく動かない部分があるので代替えとしての処理になります。

3.0 を無視するのであれば、無駄な要素が必要なく、CSS だけですべて実現可能となります。

Firefox3.0 のシェアがそれほどないことから、特に必要でなければ無視したほうが無難かもしれません。
効かなかったとしても閲覧に支障が出るわけではないからです。(効かなかったとしても、通常の overflow: hidden でテキストが切り取られるのでレイアウトは破綻しません)

では、実際にテクニックの解説です。
まずはソースを見てください。

HTML ソース

HTML はそれほど複雑ではありません。よくある形としては、様々なコンテンツを含んだ div 要素(サンプルではoverflowBox)があり、その中のどれかの要素(例えばタイトル)の文字数が未知数で、予め想定しておけない、という場合がありえると思います。
そしてそれを実現するには、その想定できない要素(つまりtext-overflowを適用させたい要素)に対してclass="overflow"を指定するのみです。
続いて CSS を見てみましょう。

CSS ソース

今回の肝はmarginだと書きました。
ざっくりとなにをしているかを説明すると、まずオーバーフローする横幅を決めます。(サンプルの場合は 180px)
そのうえで、そのブロック要素の中に、テキストの内容に応じてボックスのサイズが変わる要素を配置します。(display: inline-block) さらに、テキストがあふれない場合には...が表示されないようoverflow: hiddenを指定しておきます。

そして:after擬似要素の content に "..." を指定、それを絶対配置し、オーバーフローする横幅(180px)と同じだけの値分右にずらします。(right: -180px
そして最後に、その要素の margin-right に 100% を指定します。
この :after 擬似要素が、text-overflow のように...を表示する部分になります。

上記の内容を図にしてみました。テキストが増えても、...の位置が変わりません。
結果、テキストが溢れるくらい長くなったときにだけ「...」の部分が表示される、つまり text-overflow と同じ挙動をする、というわけです。

動作の解説

さて、ではなぜそうなるのか。

まず、margin に % を指定するとその値は親ボックスの幅(width)が基準となります。
(ちなみに、margin-top/bottom も同様に幅を基準にするので、margin はすべて幅を基準にしている、というわけです) つまり、テキストの内容に応じてボックスが拡張されることで、margin の値も変化していくのです。
そしてオーバーフローで消したいところまでボックスが伸びると、右にずらした分と margin の値が釣り合い、結果その消えていた分が顔を出す、というわけです。

このテクニックの問題点

ただこのテクニックにも問題はあり、テキストが等幅ではない場合は文字が欠けてしまう場合がある、ということと、背景に画像を使っているなど、複雑な背景の場合には使えません。
なぜなら、この文字を消す処理は、「...」を表示している要素に対して背景に色をつけ、テキストにかぶせることで消えているように見せているためです。

また、Firefox3.0 では、:after 擬似要素の position がうまく動作せず、常にテキストの最後に「...」が表示されてしまいます。
そのためサンプルでは、HTML 自身に「...」を追加し、代替案としているわけです。(サンプルで言うと .leader の部分。これはそのまま :after 擬似要素と同じことをしています)

ハックを使って Firefox だけにテクニックを指定

さらに、セレクタの部分には普段見慣れない指定があるかと思います。
これは Firefox 向けのハックです。ハックについては ヨモツネットさんの「Firefox 1.0, 1.5, 3.0, 3.5 用の CSS ハック」を参考にさせて頂きました。

そしてそれ以外にも:notなどの指定がありますが、これは IE7 対策です。
本来、セレクタのグループの中に未知のもの(サンプルではx:-moz-brokenの部分)が含まれていると、指定されているプロパティを無視するのが仕様なのですが、なぜか IE7 では無視せず、しっかりとプロパティが反映されてしまいました。

なので、実際に使いたいセレクタに、さらに:notを加えることで、それ自体も無視するようにした、というわけです。

これらの指定をすることで、text-overflow に対応しているブラウザにはそれを、Firefox だけに今回のテクニックをする、というのを実現しているわけです。

1  2  3  4  5  6  7  8  9  10