CSS Shaders はじめの 6歩目、すこし複雑な変形

Posted :

GraphicalWeb Advent Calendar 2012 の 11日目の記事です。

CSS Shaders はCSS Shaders はじめの 2歩目、custom() で何が行われるのかで解説した通り、要素をメッシュにしてその頂点を自由に変換することができるのがおおきな特徴です。

今回は、頂点を自由に操作できる特徴を利用し、CSS Shaders でしかできない変換を見ていきます。

sine のおさらい

sin(θ) についてθが増えていくとその結果は 0 … 0.5 … 1 … 0.5 のように増減を繰り返し、360度、つまり 2π で一周します。サイン波のグラフをみるとわかりやすいです。

今回はこれを利用し、頂点の z 方向が波状になるような変換をしてみます。

まずは元となる状態として次のコードを用意しました。前回の記事で 2番目紹介した内容とまったく同じです。

HTML と CSS

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CSS Shaders!</title>
<style>
.sample {
    width:500px;
    background: url( bg.png );
    -webkit-filter: custom(
        url( shader.vs ) mix( url( shader.fs ) ),
        32 32,
        u_transform perspective( 1000 ) rotateX( 45deg )
    );
}
</style>
</head>
<body>

<div class="sample">
    <p>CSS Shader!! シィーエスエス シェーダー!! </p>
    <img src="nyantocat.gif" alt="">
    <p>the Nyantocat by Cameron McEfee</p>
</div>

</body>
</html>

vertex shader

precision mediump float;

attribute vec4 a_position;
uniform mat4 u_projectionMatrix;

uniform mat4 u_transform;

void main() {
    gl_Position = u_projectionMatrix * u_transform * a_position;
}

なお、fragment shader として読み込んでいるファイルは相変わらず空です。

ここまでの状態の demo(2012年 12月現在では CSS Shader を有効にした Chrome 25以降である必要があります。)

今回は上記のうち、vertex shader のコードを操作していきます。順を追ってみていきましょう。

操作しやすいように a_position を分解する

vec4 型の変数は、4つの成分で成り立っています。そして各成分は x, y, z, w として分解するができます。今回は a_positionz だけを操作するので、ひとまず以下のように準備をしておきます。

precision mediump float;

attribute vec4 a_position;
uniform mat4 u_projectionMatrix;
uniform mat4 u_transform;

void main() {
    float z = 0;
    vec4 pos = vec4( a_position.xy, z, a_position.w );

    gl_Position = u_projectionMatrix * u_transform * pos;
}

なお、上記で、a_position.xyvec2( a_position.x, a_position.y ) と同じ意味になります。

a_meshCoord を宣言する

あとで a_meshCoord を利用したいので宣言します。a_meshCoord には、vec2 型でメッシュにおける現在処理中の頂点の xy の位置がそれぞれ 0 ~ 1 で入っています。

a_meshCoord は CSS が暗黙に渡してくれるので、shader 側で宣言するだけで利用できます。

precision mediump float;

attribute vec4 a_position;
attribute vec2 a_meshCoord;

uniform mat4 u_projectionMatrix;
uniform mat4 u_transform;

void main() {
    float z = 0;
    vec4 pos = vec4( a_position.xy, z, a_position.w );

    gl_Position = u_projectionMatrix * u_transform * pos;
}

a_meshCoord を目印に z を操作する

sin( a_meshCoord.x * 3.1415) のようにしておけば、sin( 0 ) から sin( π ) までの波を得ることができます。ただし、波の内容は 1 ~ -1 までと振れ幅が小さいので、これを大きくするために 100.0 を乗算しています。

precision mediump float;

attribute vec4 a_position;
attribute vec2 a_meshCoord;

uniform mat4 u_projectionMatrix;
uniform mat4 u_transform;

void main() {
    float z = sin( a_meshCoord.x * 3.1415) * 100.0;
    vec4 pos = vec4( a_position.xy, z, a_position.w );

    gl_Position = u_projectionMatrix * u_transform * pos;
}

これにより、次のような表示を得ることができます。いかにも CSS Shaders をつかってる感じになりました!

ここまでの状態の demo(2012年 12月現在では CSS Shader を有効にした Chrome 25以降である必要があります。)