three.js各類材質的實現源碼

three.js經常使用材質:基本材質蘭伯特材質馮氏材質標準材質git

咱們能夠本身使用着色器實現這些材質,用於批量渲染等用途。github

爲了簡單,假設物體只有一張漫反射貼圖,場景中只存在一個環境光和一個平行光。編輯器

1、基本材質(MeshBasicMaterial)

基本材質不對光源產生反應。函數

 

頂點着色器spa

varying vec2 vUv;

void main() {
    vUv = uv;
    
    vec3 transformed = vec3( position );
    vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
    gl_Position = projectionMatrix * mvPosition;
}

 

片源着色器.net

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;
}

 

2、蘭伯特材質(MeshLambertMaterial)

蘭伯特材質只有漫反射,沒有高光。code

 

頂點着色器orm

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;
}

 

片源着色器blog

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 );
}

 

3、馮氏材質(MeshPhongMaterial)

馮氏材質很重要的兩個屬性是高光顏色(specular)光亮度(shininess)three

 

頂點着色器

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 );
}

 

4、標準材質(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 );
}

 

四種材質完整實現源碼:https://gitee.com/tengge1/ShadowEditor/tree/master/ShadowEditor.Web/src/render/shader/material_simple 

 

參考資料

1. 基於three.js的開源三維場景編輯器 https://github.com/tengge1/ShadowEditor
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
相關文章
相關標籤/搜索