three.js各种材质的实现源码
three.js常用材质:基本材质、兰伯特材质、冯氏材质、标准材质。
我们可以自己使用着色器实现这些材质,用于批量渲染等用途。
为了简单,假设物体只有一张漫反射贴图,场景中只存在一个环境光和一个平行光。
一、基本材质(MeshBasicMaterial)
基本材质不对光源产生反应。
顶点着色器
varying vec2 vUv; void main() { vUv = uv; vec3 transformed = vec3( position ); vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); gl_Position = projectionMatrix * mvPosition; }
片源着色器
uniform vec3 diffuse; uniform float opacity; uniform sampler2D map; varying vec2 vUv; void main() { vec4 diffuseColor = vec4( diffuse, opacity ); vec4 texelColor = texture2D( map, vUv ); diffuseColor *= texelColor; gl_FragColor = diffuseColor; }
二、兰伯特材质(MeshLambertMaterial)
兰伯特材质只有漫反射,没有高光。
顶点着色器
uniform vec3 directColor; // 平行光颜色 uniform vec3 directDirection; // 平行光方向 #define PI 3.14159265359 varying vec2 vUv; varying vec3 vLightFront; void main() { vUv = uv; vec3 transformedNormal = normalMatrix * vec3( normal ); vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; float dotNL = dot( normalize( transformedNormal ), directDirection ); vLightFront = clamp( dotNL, 0.0, 1.0 ) * PI * directColor; }
片源着色器
uniform vec3 diffuse; // 物体颜色 uniform float opacity; // 透明度 uniform sampler2D map; uniform vec3 ambientColor; // 漫反射光颜色 varying vec2 vUv; varying vec3 vLightFront; // 双向反射PI #define RECIPROCAL_PI 0.31830988618 void main() { vec4 diffuseColor = vec4( diffuse, opacity ); vec4 texelColor = texture2D( map, vUv ); diffuseColor *= texelColor; // 出射光 = 直接漫反射 + 间接漫反射 vec3 outgoingLight = vLightFront + ambientColor * RECIPROCAL_PI * diffuseColor.rgb; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); }
三、冯氏材质(MeshPhongMaterial)
冯氏材质很重要的两个属性是高光颜色(specular)和光亮度(shininess)。
顶点着色器
varying vec2 vUv; varying vec3 vNormal; varying vec3 vViewPosition; void main() { vUv = uv; vNormal = normalize( normalMatrix * normal ); vec3 transformed = vec3( position ); vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); vViewPosition = - mvPosition.xyz; gl_Position = projectionMatrix * mvPosition; }
片源着色器
// 参考资料: // BRDF-双向反射分布函数:https://baike.baidu.com/item/双向反射分布函数/22311036 // 常见的三个光照模型:Lambert,Phong,BlinnPhong:https://blog.csdn.net/taoqilin/article/details/52800702 // 菲涅尔公式:https://baike.baidu.com/item/菲涅耳公式/9103788 // 菲涅尔折射率:https://baike.baidu.com/item/菲涅尔折射率/2712906 uniform vec3 diffuse; // 物体颜色 uniform float opacity; // 透明度 uniform vec3 specular; // 高光颜色 uniform float shininess; // 光亮度 uniform sampler2D map; uniform vec3 ambientColor; // 漫反射光颜色 uniform vec3 directColor; // 平行光颜色 uniform vec3 directDirection; // 平行光方向 varying vec2 vUv; varying vec3 vNormal; varying vec3 vViewPosition; // 双向反射PI #define RECIPROCAL_PI 0.31830988618 // 菲涅尔反射 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; } // Blinn-Phong光照模型 float D_BlinnPhong( const in float shininess, const in float dotNH ) { return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); } void main() { // 物体颜色 vec4 diffuseColor = vec4( diffuse, opacity ); vec4 texelColor = texture2D( map, vUv ); diffuseColor *= texelColor; // 环境光漫反射(BRDF兰伯特漫反射) vec3 indirectDiffuse = ambientColor * RECIPROCAL_PI * diffuseColor.rgb; // 法线 vec3 normal = normalize( vNormal ); // 平行光漫反射(BRDF兰伯特漫反射) float dotNL = clamp( dot( normal, directDirection ), 0.0, 1.0 ); vec3 irradiance = dotNL * directColor; vec3 directDiffuse = irradiance * RECIPROCAL_PI * diffuseColor.rgb; // 平行光镜面反射 vec3 halfDir = normalize( directDirection + normalize( vViewPosition ) ); // 半角向量 float dotNH = clamp( dot( normal, halfDir ), 0.0, 1.0 ); float dotLH = clamp( dot( directDirection, halfDir ), 0.0, 1.0 ); vec3 F = F_Schlick( specular, dotLH ); // 菲涅尔反射 float D = D_BlinnPhong( shininess, dotNH ); // Blinn-Phong光照模型 vec3 directSpecular = F * ( 0.25 * D ); // 出射光 = 环境光漫反射 + 平行光漫反射 + 平行光镜面反射 vec3 outgoingLight = indirectDiffuse + directDiffuse + directSpecular; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); }
四、标准材质(MeshStandardMaterial)
标准材质也叫物理材质或pbr材质,很重要的两个属性是金属度(metalness)和粗糙度(roughness)。
顶点着色器
varying vec2 vUv; varying vec3 vNormal; varying vec3 vViewPosition; void main() { vUv = uv; vNormal = normalize( normalMatrix * vec3( normal ) ); vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); vViewPosition = - mvPosition.xyz; gl_Position = projectionMatrix * mvPosition; }
片源着色器
// 参考资料: // BRDF-双向反射分布函数:https://baike.baidu.com/item/双向反射分布函数/22311036 // 基于物理的渲染—更精确的微表面分布函数GGX: https://www.jianshu.com/p/be4f025aeb3c // 菲涅尔公式:https://baike.baidu.com/item/菲涅耳公式/9103788 // 菲涅尔折射率:https://baike.baidu.com/item/菲涅尔折射率/2712906 // Moving Frostbite to Physically Based Rendering 3.0: https://blog.csdn.net/wodownload2/article/details/103126247 uniform vec3 diffuse; // 物体颜色 uniform float opacity; // 透明度 uniform float metalness; // 金属度 uniform float roughness; // 粗糙度 uniform sampler2D map; uniform vec3 ambientColor; // 漫反射光颜色 uniform vec3 directColor; // 平行光颜色 uniform vec3 directDirection; // 平行光方向 varying vec2 vUv; varying vec3 vNormal; varying vec3 vViewPosition; // 双向反射PI #define RECIPROCAL_PI 0.31830988618 // 菲涅尔反射 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( const in vec3 directDirection, const in vec3 normal, const in viewDir, const in vec3 specularColor, const in float roughness ) { float alpha = pow2( roughness ); vec3 halfDir = normalize( directDirection + viewDir ); float dotNL = clamp( dot( normal, directDirection ), 0.0, 1.0 ); float dotNV = clamp( dot( normal, viewDir ), 0.0, 1.0 ); float dotNH = clamp( dot( normal, halfDir ), 0.0, 1.0 ); float dotLH = clamp( dot( directDirection, halfDir ), 0.0, 1.0 ); vec3 F = F_Schlick( specularColor, dotLH ); float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV ); float D = D_GGX( alpha, dotNH ); return F * ( G * D ); } void main() { vec4 diffuseColor = vec4( diffuse, opacity ); vec4 texelColor = texture2D( map, vUv ); diffuseColor *= texelColor; // 法线 vec3 normal = normalize( vNormal ); // 环境光 vec3 indirectDiffuse = ambientColor * RECIPROCAL_PI * diffuseColor.rgb * ( 1.0 - metalness ); // 间接漫反射 // 平行光 float dotNL = clamp( dot( normal, directDirection ), 0.0, 1.0 ); vec3 irradiance = dotNL * directColor; vec3 specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalness ); vec3 directDiffuse = irradiance * RECIPROCAL_PI * diffuseColor.rgb * ( 1.0 - metalness ); // 直接漫反射 vec3 directSpecular = irradiance * BRDF_Specular_GGX( directDirection, normal, normalize( vViewPosition ), specularColor, clamp( roughness, 0.04, 1.0 ) ); // 直接镜面反射 // 出射光 = 间接漫反射光 + 直接漫反射 + 直接镜面反射光 vec3 outgoingLight = indirectDiffuse + directDiffuse + directSpecular; gl_FragColor = vec4( outgoingLight, diffuseColor.a ); }
参考资料
1. 原文地址:Three.js各种材质的实现源码 - 超腾开源
2. BRDF-双向反射分布函数:https://baike.baidu.com/item/双向反射分布函数/22311036
6. 基于物理的渲染—更精确的微表面分布函数GGX: https://www.jianshu.com/p/be4f025aeb3c
7. Moving Frostbite to Physically Based Rendering 3.0: https://blog.csdn.net/wodownload2/article/details/103126247