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

    四种材质完整实现源码:https://gitee.com/tengge1/ShadowEditor/tree/master/ShadowEditor.Web/src/render/shader/material_simple 

    参考资料

    1. 基于three.js的开源三维场景编辑器https://github.com/tengge1/ShadowEditor
    7. Moving Frostbite to Physically Based Rendering 3.0: https://blog.csdn.net/wodownload2/article/details/103126247
     
  • 相关阅读:
    布隆过滤器
    springboot+redis实现分布式锁
    springboot+redis实现消息队列
    工作启示文章收藏
    redis常用命令
    前方的路
    分布式系统中对cookie和session的思考
    用Markdown来写自由书籍-开源技术的方案
    Centos 7.0添加yum本地安装源
    爹地,我找到了!15个极好的Linux find命令示例
  • 原文地址:https://www.cnblogs.com/tengge/p/12037574.html
Copyright © 2020-2023  润新知