CSS Shaders はじめの 9歩目、座標に応じた影っぽい色

Posted :

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

前回の記事では、頂点に対応する色を vertex shader から fragment shader に送ることができることを紹介しました。今回は、この仕組みを利用し、影らしき着色をしてみます。

今回用意したサンプルは以下です。前回と違うのは、vertex shader の色を受け渡す部分 1行のみです。

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;
attribute vec2 a_meshCoord;

uniform mat4 u_projectionMatrix;
uniform mat4 u_transform;

// fragment shader に渡すために varying 変数を宣言
varying vec4 v_color;

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

    // z の高さ ( 0 ~ 100 ) に応じて、透明度の異なる黒を fragment shader に送る
    v_color = vec4( 0.0, 0.0, 0.0, min( 0.75, 1.0 - ( z / 100.0 ) ) );
    gl_Position = u_projectionMatrix * u_transform * pos;
}

fragment shader

precision mediump float;

// vertex shader から受け取るために varying 変数を宣言
varying vec4 v_color;

void main() {
    css_MixColor = v_color;
}

vertex shader によりメッシュ (HTML の要素) の頂点は変換され、z 方向については、0 から 100 の間で出力されています。そこで、これを利用し「z が 0 に近ければ暗く、100 に近ければ明るい」という簡素な影らしき表示をしています。(リアルな影なら光の方向と法線の方向から計算するべきですが..。)

これにより、上記の vertex shader の

// z の高さ ( 0 ~ 100 ) に応じて、透明度の異なる黒を fragment shader に送る
v_color = vec4( 0.0, 0.0, 0.0, min( 0.75, 1.0 - ( z / 100.0 ) ) );

では、色は 0, 0, 0 で黒の固定、透明度は min( 0.75, 1.0 - ( z / 100.0 ) )z の値に応じて v_color へ代入されます。

  • z0 なら vec4( 0.0, 0.0, 0.0, min( 0.75, 1.0 - ( 0.0 / 100.0 ) ); で 75% 不透明の黒色
  • z50 なら vec4( 0.0, 0.0, 0.0, min( 0.75, 1.0 - ( 50.0 / 100.0 ) ); で 50% 半透明の黒色
  • z100 なら vec4( 0.0, 0.0, 0.0, min( 0.75, 1.0 - ( 100.0 / 100.0 ) ); で透明の黒色

といった具合に色が fragment shader に渡されることになり、これによりそれっぽい感じにみせることができています。

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

CSS3 3D Transforms も CSS Shader に似た 3次元の変換はできるのですが、同時に影を表現することはできません。しかし、CSS Shaders なら変換に応じた影の表現もできるのです。