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 // 包含着色器公共模块(包含常用的数学工具函数以及一些常量定义什么的)
#include // 包含处理uv所需要的一些定义
#include // 包含处理uv2所需要的一些定义
#include // 包含置换贴图displacementmap所需要的定义
#include // 包含顶点颜色所需要的定义
#include // 包含雾化效果所需要的定义
#include // 包含变形动画所需要的定义
#include // 包含蒙皮动画所需要的定义
#include // 包含阴影计算所需要的定义
#include // 包含深度处理的一些定义
#include // 包含裁剪平面所需要的一些定义
void main() {
#include // uv 数据处理
#include // uv2 数据处理
#include // 颜色 数据处理
#include // 开始法线处理
#include // 变形动画法线处理
#include // 骨骼蒙皮基本运算
#include // 骨骼蒙皮法线运算
#include // 默认法线处理
#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
vNormal = normalize( transformedNormal );
#endif
#include // 开始顶点位置处理
#include // 变形动画位置处理
#include // 蒙皮顶点处理
#include // 置换贴图运用顶点处理
#include // 投影顶点运算
#include // logDepth深度运算
#include // 裁剪平面运算
vViewPosition = - mvPosition.xyz;
#include // 世界坐标运算
#include // 阴影所需要的一些运算
#include // 雾化所需要的运算
}
下面讲解这个顶点着色器包含的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
本文摘自:https://blog.csdn.net/weixin_39594312/article/details/111530318
ThreeJS 物理材质shader源码分析(像素着色器)
#define PHYSICAL
uniform vec3 diffuse; // 漫反射颜色
uniform vec3 emissive; // 自发光颜色
uniform float roughness; // 粗糙度
uniform float metalness; // 金属性
uniform float opacity; // 透明度
#ifndef STANDARD
uniform float clearCoat; //
uniform float clearCoatRoughness;
#endif
varying vec3 vViewPosition; // 摄像机空间的坐标
#ifndef FLAT_SHADED
varying vec3 vNormal; // 摄像机空间的法线
#endif
#include // 包含着色器公共模块(包含常用的数学工具函数以及一些常量定义什么的)
#include // 数据编码解码功能函数
#include // 抖动处理的定义
#include // 颜色处理的定义
#include // uv相关处理的定义
#include // uv2相关处理的定义
#include // map贴图相关处理的定义
#include // alphamap贴图的处理定义
#include // aomap贴图的处理定义
#include // lighmap贴图处理定义
#include // emissivemap贴图处理的定义
#include // envmap贴图处理的定义
#include // 雾化需要的定义
#include // brdf相关的功能函数
#include // cubemap反射相关
#include // 灯光相关定义
#include // 灯光贴图相关
#include // 灯光相关物理运算
#include // shadowmap影子相关运算定义
#include // bumpmap相关运算的定义
#include // normalmap相关运算的定义
#include // roughnessmap相关运算的定义
#include // metalnessmap相关运算的定义
#include // logdepth相关运算的定义
#include // clipplane裁剪平面相关的定义
void main() {
#include // 裁剪平面裁剪
vec4 diffuseColor = vec4( diffuse, opacity );// 合成rgba四通道漫反射颜色
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
vec3 totalEmissiveRadiance = emissive;
#include // logdepth运算
#include // map通道颜色采样
#include // color参与计算
#include // alphamap通道颜色采样
#include // alpha测试
#include // 粗糙贴图采样
#include // 金属性贴图采样
#include // 法线贴图基本运算
#include // 法线通过法线贴图运算
#include // 自发光贴图采样
// accumulation
#include // 物理光照基础运算
#include // 计算各种灯光入射光和反射光信息
#include // 从环境光和光照贴图获取辐射
#include // 根据辐射光取得反射信息
// modulation
#include // 根据AO贴图调整反射光照强度
// 反射光直接漫反射+间接漫反射+直接高光+间接高光+自发光 = 输出光照颜色
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
#include // tonemap进行曝光
#include // 颜色编码
#include // 雾化颜色运算
#include // 颜色预乘alpha
#include // 颜色随机抖动
}
我将这个fragmentshader提取了关于物理材质着色的核心算法方便理解代码如下:
核心算法只包含直接照明产生的漫反射颜色和高光颜色,直接照明只计算了点光源(没有计算距离衰减),去掉了各种贴图采样数据以最简化shader代码
// 由vertexshader传递过来的法线,位置,uv
varying vec3 vNormal;
varying vec3 vPosition;
varying vec2 vUv;
// 材质参数
uniform vec3 diffuse; // 漫反射
uniform float metallic; // 金属性
uniform float roughness; // 粗糙度
#if NUM_POINT_LIGHTS > 0
// 点光源信息
struct PointLight {
vec3 position;
vec3 color;
float distance;
float decay;
};
uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
#endif
#if NUM_DIR_LIGHTS > 0
struct DirectionalLight {
vec3 direction;
vec3 color;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
#endif
float pow2( const in float x ) { return x*x; }
const float PI = 3.14159265359;
#define EPSILON 1e-6
#define MAXIMUM_SPECULAR_COEFFICIENT 0.16
#define DEFAULT_SPECULAR_COEFFICIENT 0.04
#define RECIPROCAL_PI 0.31830988618
// 光照反射信息(直接光的漫反射和高光色)
struct ReflectedLight {
vec3 directDiffuse;
vec3 directSpecular;
vec3 indirectDiffuse;
};
// 入射光照信息(颜色和方向)
struct IncidentLight {
vec3 color;
vec3 direction;
};
// 几何信息(位置,法线,视角方向)
struct GeometricContext {
vec3 position;
vec3 normal;
vec3 viewDir;
};
// 物理材质信息
struct PhysicalMaterial {
vec3 diffuseColor;
float specularRoughness;
vec3 specularColor;
};
vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {
float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );
return ( 1.0 - specularColor ) * fresnel + specularColor;
}
float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
float a2 = pow2( alpha );
float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
return 0.5 / max( gv + gl, EPSILON );
}
float D_GGX( const in float alpha, const in float dotNH ) {
float a2 = pow2( alpha );
float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;
return RECIPROCAL_PI * a2 / pow2( denom );
}
vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
const vec4 c0 = vec4( - , - 0.0275, - 0.572, 0.022 );
const vec4 c1 = vec4( , 0.0425, 1.04, - 0.04 );
vec4 r = roughness * c0 + c1;
float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
return specularColor * AB.x + AB.y;
}
vec3 BRDF_Specular_GGX( in GeometricContext geometry, in IncidentLight directLight,const in vec3 specularColor, const in float roughness) {
float alpha = pow2( roughness );
vec3 halfDir = normalize( directLight.direction + geometry.viewDir );
float dotNL = saturate( dot( geometry.normal, directLight.direction ) );
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
float dotNH = saturate( dot( geometry.normal, halfDir ) );
float dotLH = saturate( dot( directLight.direction, halfDir ) );
vec3 F = F_Schlick( specularColor, dotLH );
float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
float D = D_GGX( alpha, dotNH );
return F * ( G * D );
}
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
return RECIPROCAL_PI * diffuseColor;
}
float clearCoatDHRApprox( const in float roughness, const in float dotNL ) {
return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );
}
void RE_Direct_Physical(in GeometricContext geometry,in PhysicalMaterial material, in IncidentLight directLight,inout ReflectedLight reflectedLight ) {
float dotNL = saturate( dot( geometry.normal,directLight.direction ) );// lambert漫反射因子
vec3 irradiance = dotNL * directLight.color; // 辐射
irradiance *= PI; // * PI
reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
// 重点就是这里了,高光算法和blinn-phong差距巨大
reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX( geometry,directLight,material.specularColor,material.specularRoughness);
}
void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
}
vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {
vec3 irradiance = ambientLightColor;
irradiance *= PI;
return irradiance;
}
#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical
void main() {
// 存放几何数据
GeometricContext geometry;
geometry.position = vPosition;
geometry.normal = normalize(vNormal);
geometry.viewDir = normalize(-vPosition); // 因为是相机空间只需要对位置坐标取反0-vPosition
// 存放物理材质信息
PhysicalMaterial material;
material.diffuseColor = diffuse * ( 1.0 - metallic ); // 金属性越强漫反射颜色越小
material.specularRoughness = clamp( roughness, 0.04, 1.0 ); // 粗燥度
material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuse.rgb, metallic ); // 这里做了个mix 金属性为0高光颜色也不至于是黑色
// 初始化光照反射
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ),vec3(0.0) );
IncidentLight directLight;// 直接入射光照
#if NUM_POINT_LIGHTS > 0
for(int i = ; i < NUM_POINT_LIGHTS; ++i)
{
directLight.color = pointLights[i].color; // 入射光颜色
directLight.direction = normalize(pointLights[i].position - vPosition); // 入射光的方向
RE_Direct_Physical(geometry,material,directLight,reflectedLight); // 计算直接光产生的反射光信息
}
#endif
#if NUM_DIR_LIGHTS > 0
for(int i = ; i < NUM_DIR_LIGHTS; ++i)
{
directLight.color = directionalLights[i].color; // 入射光颜色
directLight.direction = directionalLights[i].direction;
RE_Direct_Physical(geometry,material,directLight,reflectedLight); // 计算直接光产生的反射光信息
}
#endif
vec3 irradiance = getAmbientLightIrradiance( vec3(0.13) );
RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );
// 反射光信息加合为最后颜色
vec3 color = reflectedLight.directDiffuse + reflectedLight.directSpecular+reflectedLight.indirectDiffuse;
// HDR tonemapping
color = saturate(color/(color+vec3(1.0)));
// gamma correct
color = pow(color, vec3(1.0/2.0));
gl_FragColor = vec4(color, 1.0);
}
本文摘自:https://www.bbsmax.com/A/mo5kP8nEdw/