ThreeJS 物理材質shader源碼分析(頂點着色器)


ThreeJS 物理材質shader源碼分析(頂點着色器)

Threejs將shader代碼分為ShaderLib和ShaderChunk兩部分,ShaderLib通過組合ShaderChunk的代碼來構建vertexShader和fragmentShader。下面主要分析物理材質的shader源碼,他主要的兩個文件在shaderLib里面的meshphysical_vert.glsl和meshphysical_frag.glsl,前面的文件是頂點着色器,后者是像素着色器

 

1.頂點着色器meshphysical_vert.glsl

#define PHYSICAL            // 定義物理材質的宏

 

varying vec3 vViewPosition; // 相機空間每個頂點的位置

 

#ifndef FLAT_SHADED

 

    varying vec3 vNormal;   // 相機空間法線方向

 

#endif

 

#include <common>           // 包含着色器公共模塊(包含常用的數學工具函數以及一些常量定義什么的)

#include <uv_pars_vertex>   // 包含處理uv所需要的一些定義

#include <uv2_pars_vertex>  // 包含處理uv2所需要的一些定義

#include <displacementmap_pars_vertex> // 包含置換貼圖displacementmap所需要的定義

#include <color_pars_vertex>            // 包含頂點顏色所需要的定義

#include <fog_pars_vertex>              // 包含霧化效果所需要的定義

#include <morphtarget_pars_vertex>      // 包含變形動畫所需要的定義

#include <skinning_pars_vertex>         // 包含蒙皮動畫所需要的定義

#include <shadowmap_pars_vertex>        // 包含陰影計算所需要的定義

#include <logdepthbuf_pars_vertex>      // 包含深度處理的一些定義

#include <clipping_planes_pars_vertex>  // 包含裁剪平面所需要的一些定義

 

void main() {

 

    #include <uv_vertex>            // uv 數據處理

    #include <uv2_vertex>           // uv2 數據處理

    #include <color_vertex>         // 顏色 數據處理

 

    #include <beginnormal_vertex>   // 開始法線處理

    #include <morphnormal_vertex>   // 變形動畫法線處理

    #include <skinbase_vertex>      // 骨骼蒙皮基本運算

    #include <skinnormal_vertex>    // 骨骼蒙皮法線運算

    #include <defaultnormal_vertex> // 默認法線處理

 

#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED

 

    vNormal = normalize( transformedNormal );

 

#endif

 

    #include <begin_vertex>         // 開始頂點位置處理

    #include <morphtarget_vertex>   // 變形動畫位置處理

    #include <skinning_vertex>          // 蒙皮頂點處理

    #include <displacementmap_vertex>   // 置換貼圖運用頂點處理

    #include <project_vertex>           // 投影頂點運算

    #include <logdepthbuf_vertex>       // logDepth深度運算

    #include <clipping_planes_vertex>   // 裁剪平面運算

 

    vViewPosition = - mvPosition.xyz;

 

    #include <worldpos_vertex>      // 世界坐標運算

    #include <shadowmap_vertex>     // 陰影所需要的一些運算

    #include <fog_vertex>           // 霧化所需要的運算

 

}

 

下面講解這個頂點着色器包含的shaderTrunk

Common.glsl

 #define PI 3.14159265359 //π(180)

#define PI2 6.28318530718 //2π(360)

#define PI_HALF 1.5707963267949 //0.5π(90度) 

#define RECIPROCAL_PI 0.31830988618 // 1/π

#define RECIPROCAL_PI2 0.15915494    // 1/2π

#define LOG2 1.442695           // log以2為底e的對數 log2E

#define EPSILON 1e-6        // 1x10的-6次方 相當於接近0的小數

 

#define saturate(a) clamp( a, 0.0, 1.0 )        // 收攏到0~1.0之間

#define whiteCompliment(a) ( 1.0 - saturate( a ) ) // 收攏后再用1.0減

// 上面定義全局常量  

float pow2( const in float x ) { return x*x; }    // 平方

float pow3( const in float x ) { return x*x*x; }    // 三次方

float pow4( const in float x ) { float x2 = x*x; return x2*x2; } // 四次方

float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); } // 三向量x,y,z三個數平均數

// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.

// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/

highp float rand( const in vec2 uv ) {      // 隨機數

    const float a = 12.9898, b = 78.233, c = 43758.5453;

    float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );

    return fract(sin(sn) * c);

}

// 入射光

struct IncidentLight {

    vec3 color;

    vec3 direction;

    bool visible;

};

// 反射光

struct ReflectedLight {

    vec3 directDiffuse;

    vec3 directSpecular;

    vec3 indirectDiffuse;

    vec3 indirectSpecular;

};

// 幾何體信息

struct GeometricContext {

    vec3 position;

    vec3 normal;

    vec3 viewDir;

};

// 變換方向

vec3 transformDirection( in vec3 dir, in mat4 matrix ) {

 

    return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );

 

}

// 逆向變換方向(一般知道worldmatrix 和 local下的normal求worldnormal可以用次方法)

// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations

vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {

 

    return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );

 

}

// 點相對平面做投影

vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {

 

    float distance = dot( planeNormal, point - pointOnPlane );

 

    return - distance * planeNormal + point;

 

}

// 判斷點在平面哪一邊

float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {

 

    return sign( dot( point - pointOnPlane, planeNormal ) );

 

}

// 線個平面相交點

vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {

 

    return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;

 

}

// 矩陣求轉置

mat3 transposeMat3( const in mat3 m ) {

 

    mat3 tmp;

 

    tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );

    tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );

    tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );

 

    return tmp;

 

}

// 線性rgb顏色值求相對亮度

// https://en.wikipedia.org/wiki/Relative_luminance

float linearToRelativeLuminance( const in vec3 color ) {

 

    vec3 weights = vec3( 0.2126, 0.7152, 0.0722 );

 

    return dot( weights, color.rgb );

 

}

 

uv數據處理

涉及的ShaderTrunk文件有uv_pars_vertex.glsl,uv_vertex.glsl

 

uv_pars_vertex.glsl

// 如果有定義這幾種貼圖就需要包含這些變量定義

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )

 

    varying vec2 vUv;           // 輸出到fragmentshader變量定義

    uniform mat3 uvTransform;   // uv變換矩陣3x3

 

#endif

 

 

uv_vertex.glsl

// 如果定義了幾種貼圖才運用下面代碼

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )

 

    vUv = ( uvTransform * vec3( uv, 1 ) ).xy; // uv運用變換矩陣后輸出到fragmentshader

 

#endif

 

uv2數據處理

uv2:頂點的第二套uv坐標

涉及的文件有uv2_pars_vertex.glsl,uv2_vertex.glsl

uv2_pars_vertex.glsl

// 如果定義了USE_LIGHTMAP和USE_AOMAP才加入下面代碼,也就是光照貼圖和AO貼圖都需要用到第二套uv

#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )

 

    attribute vec2 uv2; // 定義頂點的uv2屬性

    varying vec2 vUv2; // 定義輸出到fragmentshader uv2變量

 

#endif

uv2_vertex.glsl

// 如果定義USE_LIGHTMAP或者USE_AOMAP加入下面代碼

#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )

 

    vUv2 = uv2; // 直接將頂點的第二套uv輸出到fragmentshader

 

#endif

 

頂點顏色數據處理

涉及的ShaderTrunk文件有color_pars_vertex.glsl,color_vertex.glsl

color_pars_vertex.glsl

// 如果定義了USE_COLOR宏才加入下面代碼

#ifdef USE_COLOR

 

    varying vec3 vColor; // 定義將顏色輸出到fragmentshader的變量

 

#endif

color_vertex.glsl

// 如果定義了使用顏色變量才加入這些代碼

#ifdef USE_COLOR

 

    vColor.xyz = color.xyz; // 將輸入的頂點顏色不經過任何處理直接輸出到fragmentshader

 

#endif

置換貼圖相關運算

涉及ShaderTrunk文件有displacementmap_pars_vertex.glsl, displacementmap_ vertex.glsl

displacementmap_pars_vertex.glsl

// 如果定義了USE_DISPLACEMENTMAP則加入下面代碼

#ifdef USE_DISPLACEMENTMAP

 

    uniform sampler2D displacementMap;  // 置換貼圖采樣器

    uniform float displacementScale;    // 縮放參數

    uniform float displacementBias;     // bias因子

 

#endif

 

 

displacement_vertex.glsl

// 如果定義了USE_DISPLACEMENTMAP加入下面代碼

#ifdef USE_DISPLACEMENTMAP

    // 下面的算法主要目的是將頂點按照法線方向偏移一定距離,這個距離的控制通過采樣置換貼圖的值(dx),distance = dx*dscale+dbias;

    transformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );

 

#endif

 

霧化處理

涉及的ShaderTrunk文件fog_vars_vertex.glsl,fog_vertex.glsl

fog_vars_vertex.glsl

// 如果定義USE_FOG加入代碼

#ifdef USE_FOG

 

  varying float fogDepth; // 輸入給fragmentshader的霧化深度變量定義

 

#endif

 

fog_vertex.glsl

// 如果定義的USE_FOG加入代碼

#ifdef USE_FOG

fogDepth = -mvPosition.z; // 將攝像機空間的頂點z坐標賦值給fogDepth

#endif

變形動畫相關處理

涉及的shaderTrunk文件morphtarget_pars_vertex.glsl, morphnormal_vertex.glsl, morphtarget_vertex.glsl

morphtarget_pars_vertex.glsl

// 如果定義了USE_MORPHTARGETS加入下面代碼

#ifdef USE_MORPHTARGETS

 

    #ifndef USE_MORPHNORMALS

 

    uniform float morphTargetInfluences[ 8 ];// 變形動畫調節因子

 

    #else

 

    uniform float morphTargetInfluences[ 4 ];

 

    #endif

 

#endif

morphnormal_vertex.glsl

// 如果定義了USE_MORPHNORMALS加入代碼

#ifdef USE_MORPHNORMALS

    // 下面線性插值將法線進行調節 c = (b - a) * scale +a;

    objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];

    objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];

    objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];

    objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];

 

#endif

 

morphtarget_vertex.glsl

// 如果定義了USE_MORPHTARGETS

#ifdef USE_MORPHTARGETS

    // 對位置進行線性插值后加權 c = (b - a)*scale+a;

    transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];

    transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];

    transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];

    transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];

 

    #ifndef USE_MORPHNORMALS // 如果沒有定義USE_MORPHNORMALS才執行如下操作

 

    transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];

    transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];

    transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];

    transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];

 

    #endif

 

#endif

 

骨骼動畫

涉及的shaderTrunk文件有skinning_pars_vertex.glsl, skinbase_vertex.glsl, skinnormal_vertex.glsl, skinning_vertex.glsl

skinning_pars_vertex.glsl

// 如果定義了USE_SKINNING加入代碼

#ifdef USE_SKINNING

 

    uniform mat4 bindMatrix; // 綁定矩陣

    uniform mat4 bindMatrixInverse; // 綁定矩陣逆矩陣

 

    #ifdef BONE_TEXTURE // 如果使用了骨骼紋理(定義了BONE_TEXTURE)

 

        uniform sampler2D boneTexture; // 骨骼紋理 這里使用的float紋理

        uniform int boneTextureSize;   // 骨骼紋理大小

 

        mat4 getBoneMatrix( const in float i ) { // 獲取骨骼紋理

 

            float j = i * 4.0; // 每個像素是xyzw一個向量 四個就能組合一個像素,這里i表示骨骼id(0~xxx)

            float x = mod( j, float( boneTextureSize ) ); // 計算骨骼數據所在的x坐標

            float y = floor( j / float( boneTextureSize ) ); // 計算骨骼數據所在的y坐標

 

            float dx = 1.0 / float( boneTextureSize );  // 每個像素占寬度所在的百分比

            float dy = 1.0 / float( boneTextureSize );  // 每個像素占寬度所在的百分比

 

            y = dy * ( y + 0.5 );

            // 采樣骨骼矩陣

            vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );

            vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );

            vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );

            vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );

 

            mat4 bone = mat4( v1, v2, v3, v4 );

 

            return bone;

 

        }

 

    #else

 

        uniform mat4 boneMatrices[ MAX_BONES ];

        // 直接通過統一變量存儲骨骼矩陣獲取數據,這種方式可能受限uniform存儲的容量限制導致支持骨骼數量不會太多

        mat4 getBoneMatrix( const in float i ) {

 

            mat4 bone = boneMatrices[ int(i) ];

            return bone;

 

        }

 

    #endif

 

#endif

 

skinbase_vertex.glsl

// 如果定義了USE_SKINNING  獲取四個骨骼矩陣

#ifdef USE_SKINNING

 

    mat4 boneMatX = getBoneMatrix( skinIndex.x );

    mat4 boneMatY = getBoneMatrix( skinIndex.y );

    mat4 boneMatZ = getBoneMatrix( skinIndex.z );

    mat4 boneMatW = getBoneMatrix( skinIndex.w );

 

#endif

skinnormal_vertex.glsl

// 如果定義了USE_SKINNING使用下面代碼計算骨骼對法線的影響

#ifdef USE_SKINNING

    // 對四個骨骼矩陣進行加權

    mat4 skinMatrix = mat4( 0.0 );

    skinMatrix += skinWeight.x * boneMatX;

    skinMatrix += skinWeight.y * boneMatY;

    skinMatrix += skinWeight.z * boneMatZ;

    skinMatrix += skinWeight.w * boneMatW;

    // 應用法線和蒙皮矩陣

    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;

 

    objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;

 

#endif

 

skinning_vertex.glsl

// 如果定義USE_SKINNING加入代碼

#ifdef USE_SKINNING

    // 使用四個骨骼矩陣對頂點坐標進行加權

    vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );

 

    vec4 skinned = vec4( 0.0 );

    skinned += boneMatX * skinVertex * skinWeight.x;

    skinned += boneMatY * skinVertex * skinWeight.y;

    skinned += boneMatZ * skinVertex * skinWeight.z;

    skinned += boneMatW * skinVertex * skinWeight.w;

 

    transformed = ( bindMatrixInverse * skinned ).xyz;

 

#endif

 

陰影計算shadowmap

涉及的shaderTrunk的代碼文件shadowmap_pars_vertex.glsl, shadowmap_vertex.glsl

shadowmap_pars_vertex.glsl

// 如果定義了USE_SHADOWMAP加入下面代碼

#ifdef USE_SHADOWMAP

 

    #if NUM_DIR_LIGHTS > 0

 

        uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];// 方向光的投影矩陣

        varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];// 輸出到fragmentshader的坐標

 

    #endif

 

    #if NUM_SPOT_LIGHTS > 0

 

        uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];// 聚光燈的陰影矩陣

        varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];// 輸出到fragmentshader的坐標

 

    #endif

 

    #if NUM_POINT_LIGHTS > 0

 

        uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];// 點光源的陰影矩陣

        varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ]; // 點光源的陰影坐標

 

    #endif

 

    /*

    #if NUM_RECT_AREA_LIGHTS > 0

 

        // TODO (abelnation): uniforms for area light shadows

 

    #endif

    */

 

#endif

 

shadowmap_vertex.glsl

// 如果定義了USE_SHADOWMAP加入代碼 通過個光源的陰影投影矩陣對頂點坐標進行投影

#ifdef USE_SHADOWMAP

 

    #if NUM_DIR_LIGHTS > 0

 

    #pragma unroll_loop

    for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {

 

        vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;

 

    }

 

    #endif

 

    #if NUM_SPOT_LIGHTS > 0

 

    #pragma unroll_loop

    for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {

 

        vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;

 

    }

 

    #endif

 

    #if NUM_POINT_LIGHTS > 0

 

    #pragma unroll_loop

    for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {

 

        vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;

 

    }

 

    #endif

 

    /*

    #if NUM_RECT_AREA_LIGHTS > 0

 

        // TODO (abelnation): update vAreaShadowCoord with area light info

 

    #endif

    */

 

#endif

 

logDepth深度運算

涉及shaderTrunk文件logdepthbuf_pars_vertex.glsl, logdepthbuf_vertex.glsl

logdepthbuf_pars_vertex.glsl

// 如果定義了USE_LOGDEPTHBUF加入下面代碼

#ifdef USE_LOGDEPTHBUF

 

    #ifdef USE_LOGDEPTHBUF_EXT// 如果定義了USE_LOGDEPTHBUF_EXT

 

        varying float vFragDepth; // 輸出到fragmentshader的深度

 

    #endif

 

    uniform float logDepthBufFC;

 

#endif

logdepthbuf_vertex.glsl

// 如果定義了USE_LOGDEPTHBUF

#ifdef USE_LOGDEPTHBUF

 

    #ifdef USE_LOGDEPTHBUF_EXT// 如果定義了USE_LOGDEPTHBUF_EXT

 

        vFragDepth = 1.0 + gl_Position.w;

 

    #else

 

        gl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;

 

        gl_Position.z *= gl_Position.w;

 

    #endif

 

#endif

 

裁剪平面運算

涉及的shaderTrunk代碼clipping_planes_pars_vertex.glsl, clipping_planes_vertex.glsl

clipping_planes_pars_vertex.glsl

// 如果裁剪平面數量大於0 且沒有定義PHYSICAL和PHONG

#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )

    varying vec3 vViewPosition;// 輸出到攝像機空間的位置

#endif

 

clipping_planes_vertex.glsl

// 如果裁剪平面數量大於0 且沒有定義PHYSICAL和PHONG

#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )

    vViewPosition = - mvPosition.xyz;// 攝像機空間的位置

#endif

 

頂點投影變換

涉及shaderTrunk的文件project_vertex.glsl

project_vertex.glsl

vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); // 將local坐標轉換到攝像機空間

 

gl_Position = projectionMatrix * mvPosition; // 再將攝像機空間坐標乘以投影矩陣做投影變換轉到裁剪空間

 

頂點世界坐標變換

涉及的shaderTrunk文件

worldpos_vertex.glsl

// 如果定義USE_ENVMAP DISTANCE USE_SHADOWMAP就需要計算頂點相對世界坐標系下的位置

#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )

 

    vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );

 

#endif

 

基本的法線處理

涉及的shaderTrunk文件beginnormal_vertex.glsl,defaultnormal_vertex.glsl

beginnormal_vertex.glsl

 

vec3 objectNormal = vec3( normal );// 將頂點輸入的法線臨時存儲一個變量

 

defaultnormal_vertex.glsl

// 將local坐標下的法線轉換到攝像機坐標系下

vec3 transformedNormal = normalMatrix * objectNormal;

 

#ifdef FLIP_SIDED   // 如果定義了FLIP_SIDED 翻轉法線

 

    transformedNormal = - transformedNormal;

 

#endif

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM