3Dプログラミング講座・その6:応用その2・GLSL、カスタムシェーダー、キーボード、ファイル



同次座標がw,x,y,z,...の順番で定義されたものの説明です。順番が違うだけで、機能に変わりはありませんが・・・


ここのシェーダーの説明は少し複雑なため、先にこちらの説明を読んだ方がいいかもしれません。
3Dプログラミング講座・その8:応用その4・GLSL、シェーダー、その2


カスタムシェーダーを作ります。
サンプルはこちらこちら
ファイルはこちらこちら





コードはこちら。
CustomShader.htm

...省略...

  <X3D showStat='false' showLog='false' x='0px' y='0px' width='1000px' height='600px'> 
      <Scene DEF='scene'>
        <Viewpoint position='0 5 100'></Viewpoint>
        <Background backUrl='"texture/generic/BK.png"' bottomUrl='"texture/generic/DN.png"' frontUrl='"texture/generic/FR.png"' leftUrl='"texture/generic/LF.png"' rightUrl='"texture/generic/RT.png"' topUrl='"texture/generic/UP.png"'></Background>
        <Transform >
          <Shape>
            <Appearance>
                <Material diffuseColor=".7 .7 .7" specularColor=".5 .5 .5"></Material>
                <MultiTexture>
                <ImageTexture url='texture/earth.jpg'></ImageTexture>
                <ComposedCubeMapTexture repeatS="false" repeatT="false">
                    <ImageTexture containerField="back" url="texture/generic/BK.png"></ImageTexture>
                    <ImageTexture containerField="bottom" url="texture/generic/DN.png"></ImageTexture>
                    <ImageTexture containerField="front" url="texture/generic/FR.png"></ImageTexture>
                    <ImageTexture containerField="left" url="texture/generic/LF.png"></ImageTexture>
                    <ImageTexture containerField="right" url="texture/generic/RT.png"></ImageTexture>
                    <ImageTexture containerField="top" url="texture/generic/UP.png"></ImageTexture>
                </ComposedCubeMapTexture>
                <ImageTexture url='texture/normalMap.png'></ImageTexture>
                </MultiTexture>
                
                <ComposedShader DEF='ComposedShader'>

                  <field name='eta' type='SFFloat' value='0.75'></field>
                  <field name='bias' type='SFFloat' value='0.25'></field>
                  <field name='scale' type='SFFloat' value='1.0'></field>
                  <field name='power' type='SFFloat' value='1.5'></field>
                  <field name='specpower' type='SFFloat' value='30.0'></field>
                  <field name='tex' type='SFInt32' value='0'></field>
                  <field name='cube' type='SFInt32' value='1'></field>
                  <field name='bump' type='SFInt32' value='2'></field>
                   
                  <ShaderPart type='VERTEX'>

                        attribute vec3 position;
                        attribute vec3 normal;
                        attribute vec2 texcoord;
                        attribute vec3 tangent;
                        attribute vec3 binormal;
                        uniform vec3 light0_Direction;
                        uniform mat4 normalMatrix;
                        uniform mat4 modelViewMatrix;
                        uniform mat4 modelViewMatrixInverse;
                        uniform mat4 modelViewProjectionMatrix;
                        varying vec2 fragTexCoord;
                        varying vec3 fragEye;
                        varying vec3 fragNormal;
                        varying vec3 fragNormal2;
                        varying vec3 fragTangent;
                        varying vec3 fragBinormal;
                        varying vec3 fragLight;
                        
                        void main()
                        {

                            vec4 eye = vec4(modelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0));
                            fragEye = position - eye.xyz;
                            fragNormal = normal;
                            fragTangent = tangent;
                            fragBinormal = binormal;
                            fragLight = light0_Direction;
                            fragNormal2 = (normalMatrix * vec4(normal, 0.0)).xyz;
                            fragTexCoord = vec2(texcoord.x, 1.0 - texcoord.y);
                            gl_Position = modelViewProjectionMatrix * vec4(position, 1.0);

                        }

                  </ShaderPart>
                  <ShaderPart type='FRAGMENT'>

                        #ifdef GL_ES
                          precision highp float;
                        #endif
                        
                        uniform float eta;
                        uniform float bias;
                        uniform float scale;
                        uniform float power;
                        uniform float specpower;
                        uniform sampler2D tex;
                        uniform samplerCube cube;
                        uniform sampler2D bump;
                        varying vec2 fragTexCoord;
                        varying vec3 fragEye;
                        varying vec3 fragNormal;
                        varying vec3 fragTangent;
                        varying vec3 fragBinormal;
                        varying vec3 fragLight;
                        varying vec3 fragNormal2;
                        
                        void main()
                        {

                            vec3 eye = normalize(fragEye);
                            vec3 normal = normalize(fragNormal);
                            vec3 tangent = normalize(fragTangent);
                            vec3 binormal = normalize(fragBinormal);

                            //バンプマッピング
                            vec4 texCol = texture2D(tex, fragTexCoord);
                            vec3 bumpCol = texture2D(bump, fragTexCoord).rgb;
                            vec3 tsn = 2.0 * (normalize(bumpCol) - 0.5);
                            tsn = tsn.z * normal + tsn.y * tangent + tsn.x * binormal;
                            normal = -normalize(tsn);
                            float p = max(0.1, dot(normal, eye));
                            texCol.rgb *= p;
                            texCol.rgb += max(0.0, pow(p, 128.0)) * vec3(0.8);

                            //フレネル反射
                            //入射,反射,屈折ベクトルの計算
                            vec3 N = -normalize(tsn);    //法線ベクトル
                            vec3 I = eye;                //入射ベクトル
                            vec3 R = reflect(I, N);      //反射ベクトル
                            vec3 T = refract(I, N, eta); //屈折ベクトル

                            //反射因数の計算
                            float fresnel = bias + scale*pow(1.0 - abs(dot(I, N)), power);
                            //float fresnel = bias + scale * pow(1.0 - max(0.0, dot(I, N)), power);
                            //反射環境色の取得
                            vec4 reflecColor = textureCube(cube, R);
                            reflecColor = reflecColor * vec4(1.333);
                            reflecColor.a = 1.0;
                            //屈折環境色の計算
                            vec4 refracColor;
                            refracColor = textureCube(cube, T);
                            refracColor.a = 1.0;

                            //ディフューズ反射
                            //スペキュラー反射
                            vec3 IS = normalize((normalize(fragLight) + eye) / 2.0); //入射ベクトル
                            vec3 ID = normalize(fragLight);                          //入射ベクトル
                            vec3 NS = normalize(fragNormal2);                        //法線ベクトル
                            float spec = pow(max(0.0, dot(-IS, NS)), specpower);     
                            float diff = max(0.0, dot(-ID, NS));
                            vec3 col   = diff * vec3(0.6);
                            col += spec * vec3(1.0);
                            //vec3 col = spec * vec3(1.0);
                            vec4 lgtCol = vec4(col, 1.0);

                            //色を統合
                            vec4 cubeCol = refracColor+fresnel*(reflecColor - refracColor);
                            cubeCol.a = fresnel * 0.5 + 0.5;
                            cubeCol = clamp(cubeCol, 0.0, 1.0);
                            //gl_FragColor = cubeCol;
                            //gl_FragColor = reflecColor;
                            //gl_FragColor = refracColor;
                            //gl_FragColor = lgtCol;
                            gl_FragColor = mix(cubeCol, lgtCol, 0.4);
                            gl_FragColor = mix(gl_FragColor, texCol, 0.5);

                        }

                  </ShaderPart>
                </ComposedShader>
            </Appearance>
<!--            
            <sphere radius="30"></sphere>
//-->
            <IndexedTriangleSet DEF='node' index='...省略...'>
                <Coordinate point='...省略...'></Coordinate>
                <Normal vector='...省略...'></Normal>
                <TextureCoordinate3D point='...省略...'></TextureCoordinate3D>
                <FloatVertexAttribute name='tangent' numComponents='3' value='...省略...'></FloatVertexAttribute>
                <FloatVertexAttribute name='binormal' numComponents='3' value='...省略...'></FloatVertexAttribute>
            </IndexedTriangleSet>

          </Shape>
        </Transform>
      </Scene>
    </X3D>  

...省略...



<Appearance>タグに<MultiTexture>タグを加え、そこにテクスチャマップ、キューブマップ、法線マップを加えます。
テクスチャマップ、法線マップは、<ImageTexture>タグで、キューブマップは<ComposedCubeMapTexture>タグを使います。
カスタムシェーダーを作るため、<ComposedShader>タグを使います。
そこに<field>タグでシェーダーパラメータ(定数、変数)を定義して
<ShaderPart type='VERTEX'>で頂点シェーダーを作ります。
また<ShaderPart type='FRAGMENT'>でフラグメントシェーダーを作ります。
で、<ShaderPart>内にGLSLシェーダープログラミングをします。
条件分岐はif文は使える、switch文は使えない。ただし条件分岐は、重くなるので、組み込み関数を使って工夫してください。

 ■■■ GLSL メモ ■■■ 

基本的な型

void

no function return value

bool

Boolean

int, uint

signed/unsigned integers

float, double

single/double-precision floating-point scalar

vec2, vec3, vec4, dvec2, dvec3, dvec4

single/double-precision floating-point vectors

mat2, mat3, mat4, dmat2, dmat3, dmat4

2x2 3x3 4x4 single/double-precision floating-point matrix



属性

attribute

OpenGLプログラムから渡す変数です。頂点ごとに変化する値を渡すことができます。

組み込みのattribute変数としては、gl_Vertex, gl_Normalなどがあります。

ユーザ独自の頂点ごとに変化するデータを用意し、シェーダに渡すことが可能です。

頂点シェーダでしか使用できません。

uniform

OpenGLプログラムから渡す変数です。描画プリミティブごとに渡すことができます。

組み込みのuniform変数としては、sampler2D, samplerCubeなどがあります。

ComposedShaderタグのフィールドを渡すことができます。

頂点シェーダ、フラグメントシェーダの両方で使用できます。

varing

頂点シェーダからフラグメントシェーダに渡す変数です。

組み込みのattribute変数としては、gl_Vertex, gl_Normalなどがあります。

ユーザ独自の頂点ごとに変化するデータを用意し、シェーダに渡すことが可能です。

頂点シェーダ、フラグメントシェーダの両方で使用できます。

const

定数。



組み込み関数:Tf=float,Td= double, Tb=bool, Ti=int, Tu=uint

Tf radians(Tf degrees)

degrees to radians

Tf degrees(Tf radians)

radians to degrees

Tf sin(Tf angle)

sine

Tf cos(Tf angle)

cosine

Tf tan(Tf angle)

tangent

Tf asin(Tf x)

arc sine

Tf acos(Tf x)

arc cosine

Tf atan(Tf y, Tf x), Tf atan(Tf y_over_x)

arc tangent

Tf sinh(Tf x)

hyperbolic sine

Tf cosh(Tf x)

hyperbolic cosine

Tf tanh(Tf x)

hyperbolic tangent

Tf asinh(Tf x)

arc hyperbolic sine

Tf acosh(Tf x)

arc hyperbolic cosine

Tf atanh(Tf x)

arc hyperbolic tangent

Tf pow(Tf x, Tf y)

x^y

Tf exp(Tf x)

e^x

Tf log(Tf x)

ln

Tf exp2(Tf x)

2^x

Tf log2(Tf x)

log2

Tfd sqrt(Tfd x)

square root

Tfd inversesqrt(Tfd x)

inverse square root

Tfd abs(Tfd x) Ti abs(Ti x)

absolute

Tfd sign(Tfd x) Ti sign(Ti x)

-1.0, 0.0, or 1.0

Tfd floor(Tfd x)

nearest integer >= x

Tfd ceil(Tfd x)

nearest integer <= x

Tfd trunc(Tfd x)

nearest integer with absolute value <= absolute value of x

Tfd fract(Tfd x)

x - floor(x)

Tfd mod(Tfd x, Tfd y), Tf mod(Tf x, float y), Td mod(Td x, double y)

modulus

Tfd modf(Tfd x, out Tfd i)

separate integer and fractional parts

Tfd min(Tfd x, Tfd y), Tf min(Tf x, float y), Td min(Td x, double y)

Tiu min(Tiu x, Tiu y), Ti min(Ti x, int y), Tu min(Tu x, uint y)

minimum value

Tfd max(Tfd x, Tfd y), Tf max(Tf x, float y), Td max(Td x, double y)

Tiu max(Tiu x, Tiu y), Ti max(Ti x, int y), Tu max(Tu x, uint y)

maximum value

Tfd clamp(Tfd x, Tfd minVal, Tfd maxVal), Tf clamp(Tf x, float minVal, float maxVal)

Td clamp(Td x, double minVal, double maxVal), Tiu clamp(Tiu x, Tiu minVal, Tiu maxVal)

Ti clamp(Ti x, int minVal, int maxVal), Tu clamp(Tu x, uint minVal, uint maxVal)

min(max(x, minVal), maxVal)

Tfd mix(Tfd x, Tfd y, Tfd a), Tf mix(Tf x, Tf y, float a), Td mix(Td x, Td y, double a)

linear blend of x and y

float length(Tf x), double length(Td x)

length of vector

float distance(Tf p0, Tf p1), double distance(Td p0, Td p1)

distance between points

float dot(Tf x, Tf y), double dot(Td x, Td y)

dot product

vec3 cross(vec3 x, vec3 y), dvec3 cross(dvec3 x, dvec3 y)

cross product

Tfd normalize(Tfd x)

normalize vector to length 1

Tfd reflect(Tfd I, Tfd N)

reflection direction

Tfd refract(Tfd I, Tfd N, float eta)

refraction vector

matN transpose(matN m), dmatN transpose(dmatN m)

transpose

float determinant(matN m), double determinant(dmatN m)

determinant

matN inverse(matN m), dmatN inverse(dmatN m)

inverse



common.js

...省略...

function changeShaderParamValue(fieldElementName, value)
{
  var fieldDOMElement = document.getElementById(fieldElementName);
  if (fieldDOMElement){
    fieldDOMElement.setAttribute("value", parseFloat(value));
    var labelElement = document.getElementById(fieldElementName + "Label");
    if (labelElement){
      labelElement.innerHTML = value;
    }
  }
}

...省略...



また、シェーダーパラメータを変更するには上の関数を呼んでください。
次のようなやり方もあります。

var cdDOMNode;
var cdObj;
cdDOMNode = document.getElementById("cd");  //ID名
cdObj = cdDOMNode.requestFieldRef("point");  //フィールド名
cdObj.push(new x3dom.fields.SFVec3f(x, y, z)); //座標セット
cdDOMNode.releaseFieldRef("point");



fresnel2.htm

...省略...

                <ComposedShader DEF='ComposedShader'>

                  <field id='time' name='time' type='SFFloat' value='0.0'></field>
                  <field name='eta' type='SFFloat' value='0.75'></field>
                  <field name='bias' type='SFFloat' value='0.25'></field>
                  <field name='scale' type='SFFloat' value='1.5'></field>
                  <field name='power' type='SFFloat' value='2.0'></field>
                  <field name='specpower' type='SFFloat' value='30.0'></field>
                  <field name='tex' type='SFInt32' value='0'></field>
                  <field name='cube' type='SFInt32' value='1'></field>
                  <field name='bump' type='SFInt32' value='2'></field>
                   
                  <ShaderPart type='VERTEX'>

                        attribute vec3 position;
                        attribute vec3 normal;
                        attribute vec2 texcoord;
                        uniform vec3 light0_Direction;
                        uniform mat4 normalMatrix;
                        uniform mat4 modelViewMatrix;
                        uniform mat4 modelViewMatrixInverse;
                        uniform mat4 modelViewProjectionMatrix;
                        varying vec2 fragTexCoord;
                        varying vec3 fragEye;
                        varying vec3 fragNormal;
                        varying vec3 fragNormal2;
                        varying vec3 fragLight;
                       
                        void main()
                        {

                            vec4 eye = vec4(modelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0));
                            fragEye = position - eye.xyz;
                            //fragEye = -(modelViewMatrix * vec4(position, 0.0)).xyz;
                            fragLight = light0_Direction;
                            fragNormal = normal;
                            fragNormal2 = (normalMatrix * vec4(normal, 0.0)).xyz;
                            fragTexCoord = vec2(texcoord.x, 1.0 - texcoord.y);
                            gl_Position = modelViewProjectionMatrix * vec4(position, 1.0);

                        }

                  </ShaderPart>
                  <ShaderPart type='FRAGMENT'>

                        #ifdef GL_ES
                          precision highp float;
                        #endif
                        
                        uniform float time;
                        uniform float eta;
                        uniform float bias;
                        uniform float scale;
                        uniform float power;
                        uniform float specpower;
                        uniform sampler2D tex;
                        uniform samplerCube cube;
                        uniform sampler2D bump;
                        varying vec2 fragTexCoord;
                        varying vec3 fragEye;
                        varying vec3 fragNormal;
                        varying vec3 fragNormal2;
                        varying vec3 fragLight;
                        
                        //バンプマッピング
                        vec3 getBumpNrm(vec2 pnt)
                        {
                            const float SQ = 1.41421356;
                            const float PI = 3.14159265;
                            const float ca = 33.333;
                            const float cb = 0.0;
                            //vec2 cp0 = vec2(0,0);
                            vec2 cp0 = vec2(-0.85, -0.8);
                            float ca0 = 3.3 * ca;
                            vec2 cp1 = vec2(0.7, -0.9);
                            float ca1 = 0.8 * ca;
                            vec2 cp2 = vec2(-0.9, 0.8);
                            float ca2 = 0.7 * ca;
                            vec2 cp3 = vec2(0.3, 0.5);
                            float ca3 = 1.7 * ca;
                            vec2 cp4 = vec2(0.85, 0.85);
                            float ca4 = 4.5 * ca;
                            vec2 po = (pnt - vec2(0.5)) * 2.0;

                            float t = time * 4.0;
                            float z;

                            //波源0
                            vec2 p0 = po-cp0;
                            vec2 q0 = normalize(p0);
                            float m0 = mod(length(p0), 2.0) * ca0 - t;
                            z = cos(m0 * PI);
                            float a0 = (z + 1.0) / 2.0;
                            z = sin(m0 * PI);
                            float b0 = sign(z);
                            vec3 c0 = vec3(b0 * q0.x, a0, b0 * q0.y);

                            //波源1
                            vec2 p1 = po - cp1;
                            vec2 q1 = normalize(p1);
                            float m1 = mod(length(p1), 2.0) * ca1 - t;
                            z = cos(m1 * PI);
                            float a1 = (z + 1.0) / 2.0;
                            z = sin(m1 * PI);
                            float b1 = sign(z);
                            vec3 c1 = vec3(b1 * q1.x, a1, b1 * q1.y);

                            //波源2
                            vec2 p2 = po - cp2;
                            vec2 q2 = normalize(p2);
                            float m2 = mod(length(p2), 2.0) * ca2 - t;
                            z = cos(m2 * PI);
                            float a2 = (z + 1.0) / 2.0;
                            z = sin(m2 * PI);
                            float b2 = sign(z);
                            vec3 c2 = vec3(b2 * q2.x, a2, b2 * q2.y);

                            //波源3
                            vec2 p3 = po - cp3;
                            vec2 q3 = normalize(p3);
                            float m3 = mod(length(p3), 2.0) * ca3 - t;
                            z = cos(m3 * PI);
                            float a3 = (z + 1.0) / 2.0;
                            z = sin(m3 * PI);
                            float b3 = sign(z);
                            vec3 c3 = vec3(b3 * q3.x, a3, b3 * q3.y);

                            //波源4
                            vec2 p4 = po - cp4;
                            vec2 q4 = normalize(p4);
                            float m4 = mod(length(p4), 2.0) * ca4 - t;
                            z = cos(m4 * PI);
                            float a4 = (z + 1.0) / 2.0;
                            z = sin(m4 * PI);
                            float b4 = sign(z);
                            vec3 c4 = vec3(b4 * q4.x, a4, b4 * q4.y);

                            //統合
                            //return vec3(c0);
                            return vec3((c0 + c1 + c2 + c3 + c4) / 5.0);
                        }

                        void main()
                        {
                            //const float center = 1.41421356;
                            const float center = 3.0;
                            vec3 eye = normalize(vec3(fragEye.x, fragEye.y, -fragEye.z));
                            
                            //バンプマッピング
                            const float ddd = 1.0;
                            vec2 pnt = fragTexCoord;
                            vec3 b0 = getBumpNrm(pnt);
                            pnt = pnt - ddd;
                            vec3 b1 = getBumpNrm(pnt);
                            pnt.x = pnt.x + 2.0 * ddd;
                            vec3 b2 = getBumpNrm(pnt);
                            pnt.y = pnt.y + 2.0 * ddd;
                            vec3 b3 = getBumpNrm(pnt);
                            pnt.x = pnt.x - 2.0 * ddd;
                            vec3 b4 = getBumpNrm(pnt);

                            //vec3 bumpNrm = b0;
                            vec3 bumpNrm = (b0 * center + b1 + b2 + b3 + b4) / (4.0 + center);
                           
                            vec3 tsn = bumpNrm;
                            vec3 ssn = bumpNrm;
                            vec3 dsn = bumpNrm;
                            tsn.y = tsn.y * 64.0;
                            ssn.y = (ssn.y - 0.5) * 4.0;
                            dsn.y = (dsn.y - 0.5) * 4.0;
                            tsn = normalize(tsn);
                            ssn = normalize(ssn);
                            dsn = normalize(dsn);
                    
                            //フレネル反射
                            //入射,反射,屈折ベクトルの計算
                            vec3 N = tsn;                //法線ベクトル
                            //vec3 N = vec3(0.0,1.0,0.0);//法線ベクトル
                            vec3 I = eye;                //入射ベクトル
                            vec3 R = reflect(I, N);      //反射ベクトル
                            vec3 T = refract(I, N, eta); //屈折ベクトル

                            //反射因数の計算
                            float fresnel = bias + scale * pow(1.0 - abs(dot(I, N)), power);
                            //float fresnel = bias + scale * pow(1.0 - max(0.0, dot(I, N)), power);
                            //反射環境色の取得
                            vec4 reflecColor = textureCube(cube, R);
                            reflecColor = reflecColor * vec4(1.333);
                            reflecColor.a = 1.0;
                            //屈折環境色の計算
                            vec4 refracColor;
                            refracColor = textureCube(cube, T);
                            refracColor.a = 1.0;

                            //ディフューズ反射
                            //スペキュラー反射
                            vec3 IS = normalize((normalize(fragLight) + eye) / 2.0); //入射ベクトル
                            vec3 ID = normalize(fragLight);                          //入射ベクトル
                            vec3 NS = ssn;                                           //法線ベクトル
                            vec3 ND = dsn;                                           //法線ベクトル
                            float spec = pow(max(0.0, dot(-IS, NS)), specpower);     
                            float diff = max(0.0, dot(-ID, ND));
                            vec3 col   = diff * vec3(0.3, 0.3, 0.6);
                            col += spec * vec3(1.0);
                            vec4 lgtCol = vec4(col, 1.0);

                            //色を統合
                            vec4 cubeCol = mix(refracColor, reflecColor, fresnel);
                            cubeCol.a = fresnel * 0.5 + 0.5;
                            //cubeCol = clamp(cubeCol, 0.0, 1.0);
                            //cubeCol = cubeCol * vec4(0.2, 0.4, 0.8, 1.0);
                            //gl_FragColor = vec4(normalize((bumpNrm * 0.5) + 0.5), 1.0);
                            //gl_FragColor = cubeCol;
                            //gl_FragColor = reflecColor;
                            //gl_FragColor = refracColor;
                            //gl_FragColor = lgtCol;
                            gl_FragColor = mix(cubeCol, lgtCol, 0.4);

                        }

                  </ShaderPart>
                </ComposedShader>

...省略...



//色を統合
vec4 cubeCol = mix(refracColor, reflecColor, fresnel);
cubeCol.a = fresnel * 0.5 + 0.5;
//cubeCol = clamp(cubeCol, 0.0, 1.0);
//cubeCol = cubeCol * vec4(0.2, 0.4, 0.8, 1.0);
//gl_FragColor = vec4(normalize((bumpNrm * 0.5) + 0.5), 1.0);
//gl_FragColor = cubeCol;
//gl_FragColor = reflecColor;
//gl_FragColor = refracColor;
//gl_FragColor = lgtCol;
gl_FragColor = mix(cubeCol, lgtCol, 0.4);

ここ↑のコメントアウトされた

gl_FragColor = vec4(normalize((bumpNrm * 0.5) + 0.5), 1.0);

を有効にして、gl_FragColorの最終代入にすると
バンプマッピングテクスチャが確認できます。

同様に、最初から完成形の描画だと分かりづらいので
//gl_FragColor = cubeCol;
//gl_FragColor = reflecColor;
//gl_FragColor = refracColor;
//gl_FragColor = lgtCol;
等をコメントアウトから外して、gl_FragColorの最終代入にすると、それぞれの色がどうなっているかが、分かりやすい。


getBumpNrm(pnt)でpntのそれぞれの波源の法線の和を取得します。
pntの中央と斜め上下左右を取得して
vec3 bumpNrm = (b0 * center + b1 + b2 + b3 + b4) / (4.0 + center);
とすることで、滑らかにしています。
//反射因数の計算
float fresnel = bias + scale * pow(1.0 - abs(dot(I, N)), power);
は、 法線、入射ベクトルが向かい合っているので、dotは大概、負を返す事が予想されるので
absをかけています。また、dot=-1(入射角0°)でfresnelが最小dot=0(入射角90°)でfresnelが最大にするため、1.0から減算します。
反射因数fresnelが1.0(普通は取り得ないけど?、2.0)に近いと反射し、0.0に近いと透過します。
これで、フレネル反射を再現します。
vec4 cubeCol = mix(refracColor, reflecColor, fresnel);
cubeCol.a = fresnel * 0.5 + 0.5;
gl_FragColor = mix(cubeCol, lgtCol, 0.4);
最後に、mix()でフレネル反射色とバンプマップ&スペキュラー反射の波のきらめき色を混合して色を決めています。


ただし、イチからシェーダーを作るより、こちらのシェーダーオーバーロードの方法の方が、パフォーマンスがいいかもしれません。
3Dプログラミング講座・その8:応用その4・GLSL、シェーダー、その2


ついでに、フォグとキーボード同時入力です。
サンプルはこちら
ファイルはこちら



コードはこちら。
usaFPS.js

...省略...

var time2 = 0.0;
var add2 = 0.001;
var R = 400;
var Y = 7.5;
var Z = -500;
var U = new N6LMatrix([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);

//メインループウサギ
function GLoop2(id){
  var rt = document.getElementById('myX3DWorld').runtime;
  if(rt && 0 < rt.canvas.doc.downloadCount) {
    TMan.timer[id].setalerm(function() { GLoop2(id); }, intvl);  //メインループ再セット
    return false;
  }


  var elm = document.getElementById(IDs[act]);
  elm.setAttribute('render', false);
  act++;
  if(31 < act) act = 0;
  elm = document.getElementById(IDs[act]);
  elm.setAttribute('render', true);

  //ウサギ移動
  U = U.UnitMat();
  U.x[1].x[0] = 0;
  U.x[2].x[0] = Y;
  U.x[3].x[0] = 0;

  var ay = new N6LVector(4, true).UnitVec(2);
  U = U.RotAxis(ay, time2 * 2 * Math.PI)
  var pos = new N6LVector([1,R,0,0],true);
  pos = U.Mul(pos);
  pos.x[3] += Z;
  U = U.TranslatedMat(pos);
  pos = U.Pos();

  //apply x3dom
  var rot = U.Vector();
  var spos = pos.ToX3DOM(true);
  var srot = rot.ToX3DOM();
  var elm = document.getElementById('usaT');
  elm.setAttribute('translation', spos.toString());
  elm = document.getElementById('usaR');
  elm.setAttribute('rotation', srot.toString());

  moveobj(t, pyr); //視点処理

  //タイマーを進める
  time2 += add2;
  if(1 <= time2) time2 = 0;

  TMan.timer[id].setalerm(function() { GLoop2(id); }, intvl);  //メインループ再セット
}

...省略...

function enter() 
{
  TMan.add();
  TMan.timer[1].setalerm(function() { GLoop(1); }, intvl);  //メインループセット
  TMan.add();
  TMan.timer[2].setalerm(function() { GLoop2(2); }, intvl);  //メインループセット
}

//オブジェクト位置情報
//位置4*4マトリクス(継続パラメータ)
var A = false;
var AU = false;

var t = new N6LVector([1, 0, 0, 0], true);
var pyr = new N6LVector([1, 0, 0, 0], true); 
var d = new N6LVector([1, 0, 20, 80], true); 
//以上のように初期化してから、下の関数を呼び続ける

//ローカルトランスレートt,(1, tx, ty, tz)(新規パラメータ) 
//ピッチヨーロール(1, θp, θy, θr)(新規パラメータ) 
function moveobj(wt, wpyr) {
  if(!A) {
    if(x3domRuntime) {
      var vm = x3domRuntime.viewMatrix().inverse(); //ワールド回転行列取得
      A = new N6LMatrix().FromX3DOM(vm);
    }
    else return;
  }

  var outmat = [];
  var outv = [];
  var WA = A.MoveMat(outmat, outv, d, wpyr, wt); //目的の関数

  //値を適用
  AU = new N6LMatrix(outmat[0]);
  A = new N6LMatrix(WA);
  t = new N6LVector([1, 0, 0, 0], true);
  pyr = new N6LVector([1, 0, 0, 0], true);

  //x3domに適用
  var Vec = A.Vector();
  var pos = A.Pos();
  var eye = pos.ToX3DOM(true);
  var ori = Vec.ToX3DOM();
  var elm = document.getElementById('viewp001');
  elm.setAttribute('position', eye.toString());
  elm.setAttribute('orientation', ori.toString());

  Vec = AU.Vector();
  pos = AU.Pos();
  eye = pos.ToX3DOM(true);
  ori = Vec.ToX3DOM();
  elm = document.getElementById('usaT_1');
  elm.setAttribute('translation', eye.toString());
  elm = document.getElementById('usaR_1');
  elm.setAttribute('rotation', ori.toString());
}

var lock = false;

//キー入力
function chkKeyBoard(){
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N1'))]) {//N1Key
    t.x[1] -= 5;
  }
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N2'))]) {//N2Key
    t.x[3] += 5;
  }
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N3'))]) {//N3Key
    t.x[1] += 5;
  }
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N4'))]) {//N4Key
    pyr.x[2] -= 2.5 * (Math.PI / 180);
  }
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N5'))]) {//N5Key
    if(!lock) pyr.x[2] += 180 * (Math.PI / 180);
    lock = true;
  }
  else lock = false;
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N6'))]) {//N6Key
    pyr.x[2] += 2.5 * (Math.PI / 180);
  }
  if(KeyB.keystate[KeyB.indexof(KeyB.ToReal('VK_N8'))]) {//N8Key
    t.x[3] -= 5;
  }
};

...省略...



 ■■■ ./nas6lib/keyboard.js メモ ■■■ 
サンプルはこちら
ファイルはこちら




フォグは<Fog>タグを用います。
<body onload="initKeyBoard(TMan, function() { chkKeyBoard(); });enter();">として、キーボード初期化と開始処理をします。
initKeyBoard(tman, func)内部のKeyB.setfunc(func);で、chkKeyBoard()をキーボード呼び出し関数に登録します。
tmanはタイマーマネージャーの実体を渡します。
GLoop2()では、NPCのウサギの姿勢計算をします。
moveobj()で、ユーザーのウサギのchkKeyBoard()で、与えられた入力情報から姿勢計算をします。

keyID、キーコード表です。

本名ID、U.S.標準キーボード

0xYX

0

1

2

3

4

5

6

7

8

9

A

B

C

D

E

F

0x0X

VK_$00

VK_LBUTTON

VK_RBUTTON

VK_CANCEL : Break

VK_MBUTTON

VK_XBUTTON1

VK_XBUTTON2

VK_$07

VK_BACK : BackSpace

VK_TAB : Tab

VK_$0A

VK_$0B

VK_CLEAR

VK_RETURN : Enter

VK_$0E

VK_$0F

0x1X

VK_SHIFT : Shift

VK_CONTROL : Ctrl

VK_MENU : Alt

VK_PAUSE

VK_CAPITAL

VK_KANA

VK_$16

VK_JUNJA

VK_FINAL

VK_KANJI

VK_$1A

VK_ESCAPE : Esc

VK_CONVERT : 変換

VK_NONCONVERT : 無変換

VK_ACCEPT

VK_MODCHANGE

0x2X

VK_SPACE : Space

VK_PRIOR : PgUp

VK_NEXT : PgDn

VK_END : End

VK_HOME : Home

VK_LEFT : ←

VK_UP : ↑

VK_RIGHT : →

VK_DOWN : ↓

VK_SELECT

VK_PRINT

VK_EXECUTE

VK_SNAPSHOT : Print Screen

VK_INSERT : Ins

VK_DELETE : Del

VK_HELP

0x3X

VK_0

VK_1

VK_2

VK_3

VK_4

VK_5

VK_6

VK_7

VK_8

VK_9

VK_$3A

VK_$3B

VK_$3C

VK_$3D

VK_$3E

VK_$3F

0x4X

VK_$40

VK_A

VK_B

VK_C

VK_D

VK_E

VK_F

VK_G

VK_H

VK_I

VK_J

VK_K

VK_L

VK_M

VK_N

VK_O

0x5X

VK_P

VK_Q

VK_R

VK_S

VK_T

VK_U

VK_V

VK_W

VK_X

VK_Y

VK_Z

VK_LWIN

VK_RWIN

VK_APPS

VK_$5E

VK_SLEEP

0x6X

VK_NUMPAD0

VK_NUMPAD1

VK_NUMPAD2

VK_NUMPAD3

VK_NUMPAD4

VK_NUMPAD5

VK_NUMPAD6

VK_NUMPAD7

VK_NUMPAD8

VK_NUMPAD9

VK_MULTIPLY : numpad *

VK_ADD : numpad +

VK_SEPARATOR : numpad enter

VK_SUBTRACT : numpad -

VK_DECIMAL : numpad .

VK_DIVIDE : numpad /

0x7X

VK_F1

VK_F2

VK_F3

VK_F4

VK_F5

VK_F6

VK_F7

VK_F8

VK_F9

VK_F10

VK_F11

VK_F12

VK_F13

VK_F14

VK_F15

VK_F16

0x8X

VK_F17

VK_F18

VK_F19

VK_F20

VK_F21

VK_F22

VK_F23

VK_F24

VK_$88

VK_$89

VK_$8A

VK_$8B

VK_$8C

VK_$8D

VK_$8E

VK_$8F

0x9X

VK_NUMLOCK : Num Lock

VK_SCROLL : Scroll Lock

VK_$92

VK_$93

VK_$94

VK_$95

VK_$96

VK_$97

VK_$98

VK_$99

VK_$9A

VK_$9B

VK_$9C

VK_$9D

VK_$9E

VK_$9F

0xAX

VK_LSHIFT

VK_RSHIFT

VK_LCONTROL

VK_RCONTROL

VK_LMENU

VK_RMENU

VK_BROWSER_BACK

VK_BROWSER_FORWARD

VK_BROWSER_REFRESH

VK_BROWSER_STOP

VK_BROWSER_SERCH

VK_BROWSER_FAVORITES

VK_BROWSER_HOME

VK_VOLUME_MUTE

VK_VOLUME_DOWN

VK_VOLUME_UP

0xBX

VK_MEDIA_NEXT_TRACK

VK_MEDIA_PREV_TRACK

VK_MEDIA_STOP

VK_MEDIA_PLAY_PAUSE

VK_LAUNCH_MAIL

VK_LAUNCH_MEDIA_SELECT

VK_LAUNCH_APP1

VK_LAUNCH_APP2

VK_$B8

VK_$B9

VK_OEM_1 : [:;]

VK_OEM_PLUS : [+]

VK_OEM_COMMA : [,]

VK_OEM_MINUS : [-]

VK_OEM_PERIOD : [.]

VK_OEM_2 : [/?]

0xCX

VK_OEM_3 : [`~]

VK_$C1

VK_$C2

VK_$C3

VK_$C4

VK_$C5

VK_$C6

VK_$C7

VK_$C8

VK_$C9

VK_$CA

VK_$CB

VK_$CC

VK_$CD

VK_$CE

VK_$CF

0xDX

VK_$D0

VK_$D1

VK_$D2

VK_$D3

VK_$D4

VK_$D5

VK_$D6

VK_$D7

VK_$D8

VK_$D9

VK_$DA

VK_OEM_4 : [[{]

VK_OEM_5 : [\|]

VK_OEM_6 : []}]

VK_OEM_7 : [']

VK_OEM_8

0xEX

VK_$E0

VK_OEM_AX

VK_OEM_102 : [\_]

VK_ICO_HELP

VK_ICO_00

VK_PROCESSKEY

VK_ICO_CLEAR

VK_PACKET

VK_$E8

VK_OEM_RESET

VK_OEM_JUMP

VK_OEM_PA1

VK_OEM_PA2

VK_OEM_PA3

VK_OEM_WSCTRL

VK_OEM_CUSEL

0xFX

VK_OEM_ATTN

VK_OEM_FINISH

VK_OEM_COPY

VK_OEM_AUTO

VK_OEM_ENLW

VK_OEM_BACKTAB

VK_ATTN

VK_CRSEL

VK_EXSEL

VK_EREOF

VK_PLAY

VK_ZOOM

VK_NONAME

VK_PA1

VK_OEM_CLEAR

VK_$FF


本名ID, 別名ID

VK_RETURN, VK_ENTER

VK_ESCAPE, VK_ESC

VK_OEM_MINUS, VK_-

VK_OEM_7, VK_^

VK_NUMPAD1, VK_N1

VK_NUMPAD2, VK_N2

VK_NUMPAD3, VK_N3

VK_NUMPAD4, VK_N4

VK_NUMPAD5, VK_N5

VK_NUMPAD6, VK_N6

VK_NUMPAD7, VK_N7

VK_NUMPAD8, VK_N8

VK_NUMPAD9, VK_N9

VK_NUMPAD0, VK_N0

VK_DECIMAL, VK_N.

VK_ADD, VK_N+

VK_SUBTRACT, VK_N-

VK_MULTIPLY, VK_N*

VK_DIVIDE, VK_N/

VK_NUMLOCK, VK_NLK

VK_OEM_5, VK_|

VK_OEM_3, VK_@

VK_OEM_4, VK_[

VK_OEM_PLUS, VK_;

VK_OEM_1, VK_:

VK_OEM_6, VK_]

VK_OEM_COMMA, VK_,

VK_OEM_PERIOD, VK_.

VK_OEM_2, VK_/

VK_OEM_102, VK__

VK_CONTROL, VK_CTRL

VK_MENU, VK_ALT

VK_CONVERT, VK_CNVT

VK_NONCONVERT, VK_NONCNVT

VK_PRIOR, VK_PGUP

VK_NEXT, VK_PGDN

VK_LEFT, VK_←

VK_UP, VK_↑

VK_RIGHT, VK_→

VK_DOWN, VK_↓

VK_INSERT, VK_INS

VK_DELETE, VK_DEL

VK_SCROLL, VK_SLK

VK_SNAPSHOT, VK_PRTSCRN

VK_OEM_ATTN, VK_CLK

VK_OEM_COPY, VK_KANA

VK_OEM_ENLW, VK_ZEN

VK_OEM_AUTO, VK_ZEN2

VK_PAUSE, VK_BRK

VK_CLEAR, VK_CLS

---

---

---

---

---

---


本名IDと別名IDの相互変換はKeyB.ToReal(str)とKeyB.ToAlias(str, ary){配列aryに別名リストが返る}でできます。
if(KeyB.keystate[KeyB.indexof(KeyB.ToReal(str))])でキーボードの押し下げ判定ができます。
KeyB.addAlias([元IDstr, 別IDstr])で別名追加定義。KeyB.delAlias(str)で別名の紐付けも一緒に削除。
KeyB.addUnityAlias([統一されたIDstr, 別IDstr, ...])で統一別名追加定義。KeyB.delUnityAlias(str)で統一別名を削除。
KeyB.UnityAlias(aliasID)で別名の統一。
KeyB.isPressUnityAlias(aliasID)で統一別名の押し下げ情報取得。
unity関連で複数のキーを1つのキーとして処理できます。


そして、セーブ/ロードです。
サンプルはこちら
ファイルはこちら



コードはこちら。

usaFPSfile.htm

...省略...

<input type="file" id="inport" /><br />
<input type="button" value="save" id="export" /><br />
<a id="download" target="_blank">download usaFPSfile.txt</a><br />

...省略...


usaFPSfile.js

...省略...

function enter() 
{
  TMan.add();
  TMan.timer[1].setalerm(function() { GLoop(1); }, intvl);  //メインループセット
  TMan.add();
  TMan.timer[2].setalerm(function() { GLoop2(2); }, intvl);  //メインループセット

  var certification = 'usaFPSfile'; //ファイル認証
  var file;
  if (window.File && window.FileReader && window.FileList && window.Blob) {
    jQuery("#export").click(function(){ //セーブ
      if(!A) return;
      var id = "download";
      //データ格納
      var content = certification + '\n' + time + '\n' + time2 + '\n' + A.Str();
      // 指定されたデータを保持するBlobを作成する。
      var blob = new Blob([ content ], { "type" : "application/x-msdownload" });
      // Aタグのhref属性にBlobオブジェクトを設定し、リンクを生成
      window.URL = window.URL || window.webkitURL;
      jQuery("#" + id).attr("href", window.URL.createObjectURL(blob));
      jQuery("#" + id).attr("download", certification + ".txt");
    });

    jQuery("#inport").on("change",function(evt){ //ロード
      file = evt.target.files;
      //FileReaderの作成
      var reader = new FileReader();
      //テキスト形式で読み込む
      reader.readAsText(file[0]);
      //読込終了後の処理
      reader.onload = function(ev){
        token = reader.result.split('\n');
        if(token[0] != certification || !A || token.length < 4) { //ファイル認証
          alert('file read error');
          return;
        }
        //データ取得
        time = Number(token[1]);
        time2 = Number(token[2]);
        A = new N6LMatrix().Parse(token[3]);
      }
    });
  } else {
      alert('browser don\'t support');
  }

}


このようにして、セーブ/ロードが実現できた。
N6LType.Str()orParse()で、文字列←→N6LTypeの相互変換です
デリミタ(区切り文字)が','と' '、を使っているので
セーブ/ロードのデリミタは、それ以外の'\n'とした。
ここまでで、大体、ゲームプログラミングが出来そうです。







3Dプログラミング講座
NAS6LIB
ポリゴンテスト解説・x3dom使い方
その1:ベクトル
その2:行列
その3:クォータニオン
その4:基本総復習・実践
その5:応用その1・メッシュアニメーション、動的テクスチャ
その6:応用その2・GLSL、カスタムシェーダー、キーボード、ファイル
その7:応用その3・ゲームプログラミング、タグの動的追加
その8:応用その4・GLSL、シェーダー、その2

<<prev メッシュアニメーション、動的テクスチャ : ゲームプログラミング、タグの動的追加 next>>





戻る