Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

CSS Shadersについて調べてみる

3Dの座標変換やある程度の動作原理を勉強したので、
今度はCSS Shadersの勉強に挑戦。
色々情報を貯めていきます。

demo

Links

CSS Shaderの動作原理など

custom()

Vertex shader(以下、VS)でmeshを生成し、それにより分割。
さらにFragument shader(Pixel shader)によって変形後の色を決定する、という流れらしい。
↑はその過程の図。

VS内のa_positionなどは決められた変数名。
main関数内で呼ばれ、vertex meshで分割された各座標それぞれに対して実行される。(つまり、それぞれの頂点に対してどういう処理を行うか、がmain関数の意味)


CSSでの指定(filter: custom())方法。

custom(url('wobble.vs')                               /* wobble effect */
           mix(url('color-swipe.fs') normal source-atop), /* swipe effect */
           40 40,                                         /* mesh lines/cols */
           amplitude 60,                                  /* wobble strength */
           amount 0.0);                                   /* effect amount */

ちなみに上記例のamplitudeやamountは決められたものではなく、各シェーダ内で使用される変数名に値を渡すことを意味する。
つまり、ここの名前と値は任意のものとなる。(hoge 30.0、とかもOK)

さらに嬉しいことに、変換行列はCSS3のtransformと同じ書式で指定できる。例えば、

-webkit-filter:
    custom(
        url(vertexshader.vs) mix(url(fragmentshader.fs) normal source-atop),
        30 30,
        transform rotateX(45deg) translateY(300px)
    );

などとすると、rotateXとtranslateYを掛けあわせた変換行列がシェーダ側に渡る。
受け取り方はuniform変数のmat4型として受け取ればいい。

//uniform変数のmat4型でCSS3の変換行列を受け取れる。
uniform mat4 transform;

使い方は通常のmat4型と同様になる。


Vertex shader(サンプル)

vertexshader.vs
precision mediump float; // Required.

// ===== Built-in Per-vertex Attributes =====
attribute vec3 a_position; // The vertex's coordinates.
attribute vec2 a_texCoord; // The vertex's texture coordinate.

// ===== Built-in Parameters =====
// Uniform parameters are available to shaders and have the
// same value for each vertex and fragment.
uniform mat4 u_projectionMatrix; // The projection matrix.

// ===== CSS Parameters =====
uniform float amplitude;
uniform float amount;

// ===== Varyings =====
// Varying are set in the vertex shader and available in the
// fragment shader.
// A fragment's value for a varying is a weighted average based
// on its distance from the three vertices surrounding it.
varying vec2 v_texCoord;

// ===== Constants ======
const float rotate = 20.0;
const float PI = 3.1415926;

// ===== Helper Functions ======
mat4 rotateX(float a) {...}
mat4 rotateY(float a) {...}
mat4 rotateZ(float a) {...}

// ===== Shader Entry Point ===== //
void main()
{
    v_texCoord = a_texCoord.xy;
    vec4 pos = vec4(a_position, 1.0);

    float r = 1.0 - abs((amount - 0.5) / 0.5);
    float a = r * rotate * PI / 180.0;
    mat4 rotX = rotateX(a);
    mat4 rotY = rotateY(a / 4.0);
    mat4 rotZ = rotateZ(a / 8.0);

    float dx = 0.01 * cos(3.0 * PI * (pos.x + amount)) * r;
    float dy = 0.01 * cos(3.0 * PI * (pos.y + amount)) * r;
    float dz = 0.1 * cos(3.0 * PI * (pos.x + pos.y + amount)) * r;

    pos.x += dx;
    pos.y += dy;
    pos.z += dz;

    gl_Position = u_projectionMatrix * rotZ * rotY * rotX * pos;
}

Fragment shader(サンプル)

fragmentshader.fs
precision mediump float; // Required.
// ===== CSS Parameters =====
uniform float amplitude; // Unused in the fragment shader.
uniform float amount;
// ===== Varyings ======
varying vec2 v_texCoord;
// ===== Constants ======
const vec3 scanlineColor = vec3(1.0, 1.0, 1.0);
const float gradientHeight = 0.1;
const mat4 grayscaleMatrix = mat4(
    
);
// ==== Shader Entry Point =====
void main()
{
    // The scanline goes from the bottom of the element (1.0) to
    // just above the top of the element (-gradientHeight).
    // This makes sure the gradient is out of view at amount = 1.0.
    float scanlineTravelDistance = 1.0 + gradientHeight;
    // Scale amount from [0,1] to [0,scanlineTravelDistance].
    float scanlineAmount = amount * scanlineTravelDistance;
    // Make the scanline start at the bottom and progress upward.
    // Its position goes from [1.0, -gradientHeight].
    float scanlinePosition = 1.0 - scanlineAmount;
    if (v_texCoord.y < scanlinePosition) {
        // Make the element grayscale above the scanline.
        css_ColorMatrix = grayscaleMatrix;
    } else {
        // Apply a gradient below the scanline.
        float distanceFromScanline = v_texCoord.y - scanlinePosition;
        float gradientStrength = (gradientHeight - min(distanceFromScanline, gradientHeight)) / gradientHeight;
        css_MixColor = vec4(scanlineColor, gradientStrength);
    }
}

CSS Shaderに暗黙的に渡される変数

※「説明」部分は、W3Cの草案を独自で訳したものです。

attribute変数

変数種 変数名 説明
attribute vec4 a_position filter regionの頂点座標。
座標は、[-0.5, 0.5]の間でx, y, z軸ともに正規化されます。
attribute vec2 a_texCoord 頂点のテクスチャ座標。
座標は、両方の軸で[0, 1]の間となります。
attribute vec2 a_meshCoord mesh boxの頂点座標。
座標は、両方の軸で[0, 1]の間となります。
attribute vec3 a_triangleCoord xとyの値は、shader meshの現在の「タイル」の座標を提供します。
例えば、(0, 0)はメッシュ内のタイルの右上を表します。
xとyの値は、それぞれ [0, mesh columns] と [0, mesh rows] の間になります。
z座標は、下図に表されるように計算されます。
z座標の値は、それぞれの頂点と対応する三角形によって提供されます。
例えば、上の三角形の右下の頂点は2に、下の三角形の右下は4となるでしょう。a_triangleCoord sample

uniform変数

変数種 変数名 説明
uniform mat4 u_projectionMatrix 現在のテクスチャ座標空間先のプロジェクションマトリクス。
注: モデル変形マトリクスプロパティセットは、シェーダーに渡されません。
フィルターをかけられた描画要素に適用されます。
uniform sampler2D u_texture 入力テクスチャ。
フィルターマージンとして透明のマージンも含まれます。(※セキュリティ的な問題でアクセスできないかも?)
uniform sampler2D u_contentTexture フィルターをかけられた描画要素のテクスチャ。
もしフィルターがフィルター連鎖の最初だった場合、このテクスチャはu_texture uniform変数と同等となります。
しかしもし、それらが先行フィルターだった場合はこれはオリジナルのフィルターされた描画要素として提供されます。
一方、u_textureはフィルター連鎖(あるいはグラフ)の最初の先行フィルターとして出力されます。
uniform vec2 u_textureSize 入力テクスチャのサイズ。
フィルターマージンを含む。
uniform vec4 u_meshBox フィルターボックス座標系内のmesh boxの位置とサイズ。
例えば、もしmesh boxがフィルターボックスなら、値は(-0.5, -0.5, 1, 1)となるでしょう。
uniform vec2 u_tileSize 頂点と同じ座標空間における現在のメッシュタイルのサイズ。
uniform vec2 u_meshSize タイルの面で現在のメッシュのサイズ。
x座標は行の数値を、y座標は列の数値を提供します。

特殊変数

使ってみて使える、みたいな感じなのでとりあえずメモ程度。(ちゃんと調べてません)

対象シェーダ 変数名 説明
Vertex vec4 gl_Position 頂点位置をWebGLに伝える変数
Fragment vec4 gl_FragCoord 現在計算中のフラグメントの座標
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
13
Help us understand the problem. What are the problem?