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

2. BRDF-双向反射分布函数:https://baike.baidu.com/item/双向反射分布函数/22311036

3. 常见的三个光照模型:Lambert,Phong,BlinnPhong:https://blog.csdn.net/taoqilin/article/details/52800702

4. 菲涅尔公式:https://baike.baidu.com/item/菲涅耳公式/9103788

5. 菲涅尔折射率:https://baike.baidu.com/item/菲涅尔折射率/2712906

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

原文地址:https://www.cnblogs.com/tengge/p/12037574.html

时间: 2024-11-08 19:15:40

three.js各种材质的实现源码的相关文章

JS魔法堂:jsDeferred源码剖析

一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计>提供该资讯,再次感谢),追本溯源地了解jsDeferred是十分有必要的,并且当你看过官网(http://cho45.stfuawsc.com/jsdeferred/)的新手引导后就会有种不好好学学就太可惜的感觉了,而只看API和使用指南是无法满足我对它的好奇心的,通过解读源码读透它的设计思想才是根本.

(转)Android与js交互实例(附源码)

本文转载于:http://blog.csdn.net/ithomer/article/details/8737999# Android 中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为true Android(Java)与JavaScript(HTML)交互有四种情况: 1) Android(Java)调用HTML中js代码 2) Android(Java)调用HTML中js代码(带参数) 3) HTML中js调用Android(

JS魔法堂:mmDeferred源码剖析

一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见:[email protected] 二.API说明 {Deferred} Deferred({Function|Object} mixin?) ,创建一个Deferred实例,当mixin的类型为Object时,将mixin的属性和函数附加到Deferred实例的Promise实例上. {Stri

百度地图 api 功能封装类 (ZMap.js) 本地搜索,范围查找实例 [源码下载]

相关说明 1. 界面查看: 吐槽贴:百度地图 api 封装 的实用功能 [源码下载] 2. 功能说明: 百度地图整合功能分享修正版[ZMap.js] 实例源码! ZMap.js 本类方法功能大多使用 prototype 原型 实现: 包含的功能有:轨迹回放,圈画区域可编辑,判断几个坐标是否在一个圆圈内,生活服务查询,从经纬度获取地址信息,地图工具包括测距,获取面积,以积打印地图,地图全屏,实时路况,坐标是否在polygon区域内,打车方案,经过中间途经点,添加地图控件: 界面预览 本界面项目由:

百度地图 api 功能封装类 (ZMap.js) 新增管理事件功能 [源码下载]

ZMap 功能说明 ZMap.js 本类方法功能大多使用 prototype 原型 实现: 包含的功能有:轨迹回放,圈画区域可编辑,判断几个坐标是否在一个圆圈内,生活服务查询,从经纬度获取地址信息,地图工具包括测距,获取面积,以积打印地图,地图全屏,实时路况,坐标是否在polygon区域内,打车方案,经过中间途经点,添加地图控件: 界面查看 : http://www.cnblogs.com/editor/p/4080517.html 相关新增代码 1. defaults 代码块: ZMap.de

js封装、简单实例源码记录

1.运动封装:doMove ( obj, attr, dir, target, endFn )  加入回调.&&.||用法注释 <script> var oBtn1 = document.getElementById('btn1'); var oDiv = document.getElementById('div1'); oBtn1.onclick = function () { // doMove ( oDiv, 'width', 34, 600 ); doMove ( oD

axis2 webservice入门知识(JS,Java,PHP调用实例源码)

背景简介 最近接触到一个银行接口的案子,临时需要用到axis2 webservice.自己现学现总结的一些东西,留给新手.少走弯路. Axis2简介 ①采用名为 AXIOM(AXIs Object Model)的新核心 XML 处理模型,利用新的XML解析器提供的灵活性按需构造对象模型. ②支持不同的消息交换模式.目前Axis2支持三种模式:In-Only.Robust-In和In-Out.In-Only消息交换模式只有SOAP请求,而不需要应答:Robust-In消息交换模式发送SOAP请求,

JS 数组方法splice的源码探究

Array.splice()splice() 方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改.var arr=[1,2,3,4]arr.splice(0,1)=[1]                 arr=[2,3,4]            删除arr.slice(0,1)=[2,3,4]             arr=[1,2,3,4]arr.splice(0,-1)=[]                  arr=[1,2,3,4]  arr.s

从Vue.js源码角度再看数据绑定

## 写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:[https://github.com/answershuto/learnVue](https://github.com/answershuto/learnVue).在学习过程中,为Vue加上了中文的注释[https://github.com/answershuto/learnVue/tree/master/vue-src](http