1 Star 0 Fork 0

programmer_Luosx/3d-test1

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

web3D笔记

// Project setup
npm install

// Compiles and hot-reloads for development
npm run dev

// Compiles and minifies for production
npm run build

第一章 初步掌握

一、Three.js的三要素

场景Scene、相机Camera、渲染器Renderer

// 创建3D场景对象Scene
const scene = new THREE.Scene();

// 实例化一个透视投影相机对象,模拟人眼
const camera = new THREE.PerspectiveCamera(30, 1, 1, 3000); //视角(范围),画布宽高比,近裁截面,远裁截面
camera.position.set(200, 200, 200);  //相机位置
camera.lookAt(0, 0, 0); //相机观察目标

// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.render(scene, camera); //执行渲染操作
document.getElementById('xxId').appendChild(renderer.domElement); //画布插入到任意HTML元素中

二、光源对物体表面影响

1、网格材质

  • 不受光照影响 MeshBasicMaterial
  • 受光照影响 MeshLambertMaterial 漫反射 MeshPhongMaterial 高光反射,有镜面反射效果 MeshStandardMaterial 物理 MeshPhysicalMaterial 物理

2、光源

  • 环境光 AmbientLight
  • 点光源 PointLight
  • 聚光灯 SpotLight
  • 平行光 DirectionalLight
const pointLight = new THREE.PointLight(0xffffff); // 白光
pointLight.intensity = 1.0;//光照强度
pointLight.decay = 0.0;//设置光源不随距离衰减
pointLight.position.set(400, 200, 300);  //光照位置

光源辅助观察,例xxxxLightHelper

三、轨道控制器OrbitControls使用

通过相机控件OrbitControls实现旋转缩放预览效果。旋转:拖动鼠标左键,缩放:滚动鼠标中键,平移:拖动鼠标右键

// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement); // 设置相机控件轨道控制器

// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
    renderer.render(scene, camera);
});

四、动画渲染

threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染

function render() {
    renderer.render(scene, camera); //执行渲染操作
    requestAnimationFrame(render);//请求再次执行函数render,无限循环
}

设置了渲染循环,相机控件OrbitControls就不用再通过事件change执行renderer.render(scene, camera);因为渲染循环一直在执行renderer.render(scene, camera);。

// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// 渲染循环
const render3D = () => {
    renderer.render(scene, camera)
    requestAnimationFrame(render3D)
}

五、canvas画布宽高动态变化

threejs渲染输出的结果就是一个Cavnas画布,所以可以插入到web页面上任何一个元素中

// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
    // 重置渲染器输出画布canvas尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
    // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
    // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
    camera.updateProjectionMatrix();
};

六、性能检测stats

stats.js库可以查看three.js当前的渲染性能,计算three.js的渲染帧率(FPS)。简单说就是three.js每秒钟完成的渲染次数,一般渲染达到每秒钟60次为最佳状态。

//引入性能监视器stats.js
import Stats from 'three/addons/libs/stats.module.js';

const stats = new Stats(); //创建stats对象
document.body.appendChild(stats.domElement); //web页面上输出计算结果

function render() {
	stats.update(); // 循环调用方法update(),来刷新时间
	renderer.render(scene, camera); 
	requestAnimationFrame(render);
}
render();

可以通过stats.setMode()方法的参数mode的数值设置首次打开页面,测试结果的显示模式。鼠标单击可以更换为不同的显示模式。

七、常见几何体

//BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);

八、双面可见

Three.js的材质默认正面可见,反面不可见

new THREE.MeshBasicMaterial({
    // side: THREE.FrontSide, //默认只有正面可见
    // side: THREE.BackSide, //设置只有背面可见
    side: THREE.DoubleSide, //两面可见
});

九、WebGL渲染器设置

1、抗锯齿 antialias

const renderer =new Three.WebGLRenderer({
    antialias: true  // 默认false,开启抗锯齿图像展示更平滑
})

2、设备像素比 .setPixelRatio()

// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);  // 必写

十、可视化改变三维场景(gui.js库)

能够可视化的动态地调整设置的图像参数,便于观察变化。

1、通过.add()增加交互界面,改变obj对应属性值。

  • 参数3和参数4分别是数字,交互界面是拖动条
  • 参数3是数组,生成交互界面是下拉菜单
  • 参数3是对象,生成交互界面是下拉菜单,key为选项名,value为值
  • 改变属性的对应的数据类型是布尔值,交互界面是单选框
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
const gui = new GUI(); // 实例化一个gui对象

const obj = {x: 30, y: 60, z: 300};
gui.add(obj, 'x', 0, 100);   // 属性值范围(0-100)
gui.add(obj, 'y', 0, 50);    // 属性值范围(0-50)
gui.add(obj, 'z', 0, 60);

gui.add(ambient, 'intensity', 0, 2.0);  // 设置灯光强度
gui.add(mesh.position, 'x', 0, 180);    // 模型位置
gui.add(mesh.position, 'y', [0,10,20,30,40]);
gui.add(mesh.position, 'z', {left: -10,center: 0,right: 10});

2、通过.name('自定义名称'),对属性进行命名

gui.add(ambient, 'intensity', 0, 2.0).name('设置光强度');

3、.step()设置调节步长

gui.add(ambient, 'intensity', 0, 2.0).name('设置光强度').step(0.5)

4、.onChange()属性改变时触发对应事件

gui.add(mesh.position, 'x', 0, 180).onChange(function(value){
    console.log('mesh的x坐标:',value)
}

5、.addColor()颜色值改变

gui.addColor(light, 'color').name('设置灯光颜色')

6、.addFolder()分组,方便管理,可套娃

const posFolder = gui.addFolder('坐标')
posFolder.add(mesh.position, 'y', [0,10,20,30,40])
posFolder.add(mesh.position, 'x', 0, 50)

const lightFolder = gui.addFolder('光照')
lightFolder.add(light, 'intensity', 0, 10)
lightFolder.addColor(light, 'color')

lightFolder.close() // 折叠组
posFolder.open()    // 打开组

第二章 几何体BufferGeometry

一、几何体顶点位置数据、点模型

1、缓冲类型几何体BufferGeometry

长方体BoxGeometry、球体SphereGeometry等几何体都是基于BufferGeometry (opens new window)类构建的;

BufferGeometry是一个没有任何形状的空几何体,你可以通过定义顶点数据,BufferGeometry展示出任何几何形状。

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry(); 

//创建顶点数据,类型化数组
const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    50, 0, 0, //顶点2坐标
    0, 100, 0, //顶点3坐标
    0, 0, 10, //顶点4坐标
    0, 0, 100, //顶点5坐标
    50, 0, 10, //顶点6坐标
]);
// 创建属性缓冲区对象。3个为一组,表示一个顶点的xyz坐标
const attribue = new THREE.BufferAttribute(vertices, 3); 

// .attributes.position 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;

2、点模型对象Points

点模型Points和网格模型Mesh一样,只是大部分情况都是用Mesh表示物体。点模型Points会把几何体渲染为点,网格模型Mesh会把几何体渲染为面。

// 点渲染模式
const material = new THREE.PointsMaterial({
    color: 0xffff00,
    size: 10.0 //点对象像素尺寸
}); 
const points = new THREE.Points(geometry, material); //点模型对象

二、线模型对象

渲染效果是从第一个点开始到最后一个点,依次连成线。

// 线材质对象
const material = new THREE.LineBasicMaterial({
    color: 0xff0000 //线条颜色
}); 
const line = new THREE.Line(geometry, material); // 创建线模型对象

const line = new THREE.LineLoop(geometry, material);  // 闭合线条

const line = new THREE.LineSegments(geometry, material); //非连续的线条

三、网格模型

三角形面

网格模型Mesh其实就一个一个三角形(面)拼接构成。使用网格模型Mesh渲染几何体geometry,就是几何体所有顶点坐标三个为一组,构成一个三角形,多组顶点构成多个三角形,就可以用来模拟表示物体的表面。

眼睛(相机)对着三角形的一个面,如果三个顶点的顺序是逆时针方向,该面视为正面;反之,为反面。

  • 正面:逆时针
  • 反面:顺时针

构建一个矩形平面几何体

一个矩形平面,可以至少通过两个三角形拼接而成。而且两个三角形有两个顶点的坐标是重合的。
注意三角形的正反面问题:保证矩形平面两个三角形的正面是一样的,也就是从一个方向观察,两个三角形都是逆时针或顺时针。
const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    80, 0, 0, //顶点2坐标
    80, 80, 0, //顶点3坐标
    0, 0, 0, //顶点4坐标   和顶点1位置相同
    80, 80, 0, //顶点5坐标  和顶点3位置相同
    0, 80, 0, //顶点6坐标
]);

四、几何体顶点索引数据

// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([
    // 下面索引值对应顶点位置数据中的顶点坐标
    0, 1, 2, 0, 2, 3,
])
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组

五、顶点法线数据

六、查看threejs自带几何体顶点

查看几何体顶点位置和索引数据

可以用顶点索引index数据构建几何体,也可以不用,threejs默认的大部分几何体都有三角形的顶点索引数据,具体可以通过浏览器控制台打印几何体数据查看。

const geometry = new THREE.PlaneGeometry(100,50); //矩形平面几何体
// const geometry = new THREE.BoxGeometry(50,50,50); //长方体
console.log('几何体',geometry);
console.log('顶点位置数据',geometry.attributes.position);
console.log('顶点索引数据',geometry.index);

材质属性.wireframe

线条模式渲染,查看几何体三角形结构

const material = new THREE.MeshLambertMaterial({
    color: 0x00ffff, 
    wireframe:true, // 线条模式渲染mesh对应的三角形数据
});

几何体细分数

Three.js很多几何体都提供了细分数相关的参数。例:矩形平面几何体至少需要两个三角形拼接而成。

 // 矩形几何体PlaneGeometry的参数3,4表示细分数,默认是1,1
const geometry = new THREE.PlaneGeometry(100,50,1,1);

// 把一个矩形分为2份,每个矩形2个三角形,总共就是4个三角形
const geometry = new THREE.PlaneGeometry(100,50,2,1);

// 把一个矩形分为4份,每个矩形2个三角形,总共就是8个三角形
const geometry = new THREE.PlaneGeometry(100,50,2,2);

三角形数量与性能

对于一个曲面而言,细分数越大,表面越光滑,但是三角形和顶点数量却越多。
几何体三角形数量或者说顶点数量直接影响Three.js的渲染性能,在不影响渲染效果的情况下,一般尽量越少越好。

七、旋转、缩放、平移

BufferGeometry的旋转、缩放、平移等方法,本质上就是改变顶点的位置坐标

缩放.scale()

// 几何体xyz三个方向都放大2倍
geometry.scale(2, 2, 2);

// 几何体旋转、缩放或平移之后,查看几何体顶点位置坐标的变化
console.log('顶点位置数据', geometry.attributes.position);

平移.translate()

// 几何体沿着x轴平移50
geometry.translate(50, 0, 0);

旋转.rotateX() .rotateY() .rotateZ()

// 几何体绕着x轴旋转45度
geometry.rotateX(Math.PI / 4); // Math.PI = 3.1415926

居中.center()

geometry.translate(50, 0, 0);//偏移
// 居中:已经偏移的几何体居中,执行.center(),你可以看到几何体重新与坐标原点重合
geometry.center();

第三章 模型、材质

第四章 层级模型

第五章 顶点UV坐标、纹理贴图

纹理贴图

通过纹理贴图加载器TextureLoader的load()方法加载一张图片可以返回一个纹理对象Texture,纹理对象Texture可以作为模型材质颜色贴图.map属性的值。

//纹理贴图加载器TextureLoader, .load()方法加载图像,返回一个纹理对象Texture
const texLoader = new THREE.TextureLoader();
const texture = texLoader.load('./earth.jpg');
const material = new THREE.MeshLambertMaterial({
    map: texture, //map表示材质的颜色贴图属性
});
// material.map = texture; // 也可设置贴图

.map颜色贴图和.color属性颜色值会混合

自定义顶点UV坐标

颜色纹理贴图映射到不同的几何体上,映射效果不同。其实和UV坐标相关。

顶点UV坐标的作用是从纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。

const geometry = new THREE.PlaneGeometry(200, 100); //矩形平面
// const geometry = new THREE.BoxGeometry(100, 100, 100); //长方体
// const geometry = new THREE.SphereGeometry(100, 30, 30);//球体

console.log('uv',geometry.attributes.uv);  // 查看几何体默认UV坐标

自定义顶点UV

顶点UV坐标geometry.attributes.uv和顶点位置坐标geometry.attributes.position是一一对应的,即position有几个顶点,UV也需要设置几个顶点。
UV顶点坐标你可以根据需要在0~1之间任意设置
/**纹理坐标0~1之间随意定义*/
const uvs = new Float32Array([
    0, 0, //图片左下角
    1, 0, //图片右下角
    1, 1, //图片右上角
    0, 1, //图片左上角
]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标
const vertices = new Float32Array([
    0,0,0,
    10,0,0,
    10,10,0,
    0,0,0,
    10,10,0,
    0,10,0
])
const uvs = new Float32Array([
    0, 0, //图片左下角
    1, 0, //图片右下角
    1, 1, //图片右上角
    0, 0,
    1, 1,
    0, 1 //图片左上角
]);
const geometry = new Three.BufferGeometry()
geometry.setAttribute('uv',new Three.BufferAttribute(uvs, 2))
geometry.setAttribute('position',new Three.BufferAttribute(vertices, 3))

// 通过材质的map添加纹理
const material = new Three.MeshBasicMaterial({map: texture, side: Three.DoubleSide})
const mesh = new Three.Mesh(geometry, material)
mesh.position.set(10,10,0)
scene.add(mesh)

圆形平台设置纹理图

通过圆形几何体CircleGeometry创建一个网格模型Mesh,把一张图片作为圆形Mesh材质的颜色贴图,可以把一张方形图片剪裁四角,渲染为圆形效果。

CircleGeometry的UV坐标会对颜色纹理贴图.map进行提取,CircleGeometry的UV坐标默认提取的就是一个圆形轮廓。

纹理对象Texture阵列

使用纹理对象Texture的阵列功能+矩形平面几何体PlaneGeometry实现一个地面瓷砖效果。

设置了阵列,重复渲染纹理才会均匀分布

const geometry = new THREE.PlaneGeometry(2000, 2000);
const texLoader = new THREE.TextureLoader();
const texture = texLoader.load('./瓷砖.jpg');

//设置阵列
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
// 设置uv两个方向纹理重复数量
texture.repeat.set(10, 10)

const material = new THREE.MeshLambertMaterial({
    map: texture,
});
const mesh = new THREE.Mesh(geometry, material);

矩形Mesh+背景透明png贴图

把一个背景透明的.png图像作为平面矩形网格模型Mesh的颜色贴图是一个非常有用的功能,可以对三维场景进行标注。

整体思路:创建一个矩形平面,设置颜色贴图.map,注意选择背景透明的.png图像作为颜色贴图,同时材质设置transparent: true,这样png图片背景完全透明的部分不显示。

const geometry = new THREE.PlaneGeometry(60, 60); //默认在XOY平面上
const textureLoader = new THREE.TextureLoader();
const material = new THREE.MeshBasicMaterial({
    map: textureLoader.load('./left.png'),        
    transparent: true, //开启透明
});
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(-Math.PI / 2);

UV动画

纹理对象Texture的.offset的功能是偏移贴图在Mesh上位置,本质上相当于修改了UV顶点坐标。

function render() {
    texture.offset.x +=0.01; //设置纹理动画:偏移量根据纹理和动画需要,设置合适的值
    // mesh.position.x -= 0.2
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

第六章 加载三维模型(gltf)

一、建模软件绘制3D场景(Blender)

3D美术常用的三维建模软件,比如Blender、3dmax、C4D、maya等等

分工和流程:

  • 3D美术:使用三维建模软件绘制3D模型,导出gltf等常见格式
  • 程序:加载解析三维软件导出的三维模型 比如使用Blender三维建模软件导出gltf格式模型,然后再通过threejs加载三维模型。

gltf(Web3D的jpg)

GLTF格式是新2015发布的三维模型格式,随着物联网、WebGL、5G的进一步发展,会有越来越多的互联网项目Web端引入3D元素,你可以把GLTF格式的三维模型理解为.jpg、.png格式的图片一样是标配。在2017年又发布了GLTF2.0的版本。

GLTF文件通过JSON的键值对方式来表示模型信息。.gltf格式文件几乎可以包含所有的三维模型相关信息的数据,比如模型层级关系、PBR材质、纹理贴图、骨骼动画、变形动画...

不仅three.js,其它的WebGL三维引擎cesium、babylonjs都对gltf格式有良好的的支持。

.bin文件、.glb文件

glTF文件会关联一个或多个.bin文件,.bin文件以二进制形式存储了模型的顶点数据等信息。

.glb是gltf格式的二进制文件。.gltf模型和贴图信息.bin全部合成得到一个.glb文件。.glb文件相对.gltf文件体积更小,网络传输自然更快。

二、加载.gltf文件(模型加载全流程)

在实现基础场景、光源、渲染器、相机后,三步:

  • gltf模型加载器GLTFLoader.js
  • 相机参数根据需要设置
  • 加载gltf的时候,webgl渲染器编码方式设置
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 创建GLTF加载器对象
const loader = new GLTFLoader();

// gltf对象包含模型、动画等信息,gltf.scene属性包含的是模型信息
loader.load( 'gltf模型.gltf', function ( gltf ) {
  scene.add( gltf.scene );  // 返回的场景对象gltf.scene插入到threejs场景中
})

尺寸

一般通过三维建模软件可以轻松测量模型尺寸,用三维建模软件blender打开gltf模型,测量尺寸。

three.js的世界没有任何单位,只有数字大小的运算。obj、gltf格式的模型信息只有尺寸,并不含单位信息。

不过实际项目开发的时候会约定单位,如果单位不统一,可以用.scale缩放

相机注意

  • 相机的位置合理设置.position
  • 某位在居中,camera.lookAt()指向该坐标(OrbitControls会影响lookAt,orbitControls.target应与lookAt相同)
  • PerspectiveCamera(fov, aspect, near, far)近远截面合理设置,影响可视范围

纹理贴图颜色偏差解决

three.js加载gltf模型的时候,可能会渲染结果颜色偏差,对于这种情况,只需要修改WebGL渲染器默认的编码方式.outputColorSpace 即可

// 设置为SRGB颜色空间
renderer.outputColorSpace = THREE.sRGBEncoding; // 旧版用.outputEncoding 

递归模型修改材质

加载一个外部模型,比如gltf模型,如果你想批量修改每个Mesh的材质,一个一个设置比较麻烦,可以通过递归遍历方法.traverse()批量操作更加方便

// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function(obj) {
    if (obj.isMesh) { //判断是否是网格模型
        console.log('模型节点名字',obj.name);
    }
});

threejs解析gltf模型默认材质一般是MeshStandardMaterial或MeshPhysicalMaterial,这两个材质属于PBR物理材质,可以提供更加真实的材质效果

gltf.scene.traverse(function(obj) {
    if (obj.isMesh) {
        // 重新设置材质
        obj.material = new THREE.MeshLambertMaterial({
            color:0xffffff,
        });
    }
});

外部模型材质共享的问题

美术通过三维建模软件绘制好一个三维场景以后,一些外观一样的Mesh,可能会共享一个材质对象。因此出现改变一个模型颜色其它模型跟着变化

解决问题方向

  • 三维建模软件中设置,需要代码改变材质的Mesh不要共享材质,要独享材质。
  • 代码批量更改:克隆材质对象,重新赋值给mesh的材质属性
//用代码方式解决mesh共享材质问题
gltf.scene.getObjectByName("小区房子").traverse(function (obj) {
    if (obj.isMesh) {
        // .material.clone()返回一个新材质对象,和原来一样,重新赋值给.material属性
        obj.material = obj.material.clone();
    }
});
mesh1.material.color.set(0xffff00);
mesh2.material.color.set(0x00ff00);

第七章 PBR材质与纹理贴图

PBR材质简介

PBR基于物理的渲染(physically-based rendering)。

Three.js提供了两个PBR材质相关的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial是MeshStandardMaterial扩展的子类,提供了更多功能属性。他们用的光照模型不同,反射光照的代码算法不同,算法不同,自然模拟光照的真实程度也不同。

  • MeshLambertMaterial: Lambert光照模型(漫反射)
  • MeshPhongMaterial:Phong光照模型(漫反射、高光反射)
  • MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理论、能量守恒、菲涅尔反射...)

PBR材质金属度和粗糙度

金属度属性.metalness表示材质像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0。 threejs的PBR材质,.metalness默认是0.5,0.0到1.0之间的值可用于生锈的金属外观

粗糙度roughness表示模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。 粗糙度roughness,0.0表示平滑的镜面反射,1.0表示完全漫反射,默认0.5。

new THREE.MeshStandardMaterial({
    metalness: 1.0,//金属度属性
    roughness: 0.5,//表面粗糙度
})

环境贴图.envMap(金属效果)

环境贴图对PBR材质渲染效果影响还是比较大,一般渲染PBR材质的模型,最好设置一个合适的环境贴图

Shader着色器

Shader可以做什么:

  • 建筑流光效果
  • 智慧城市特效
  • 地球、地图可视化飞线Shader
  • 网页波浪背景

需要了解WebGL的着色器GLSL ES语言

ShaderMaterial着色器材质

与MeshBasicMaterial、MeshLambertMaterial这些材质通过color、map设置外观不同

ShaderMaterial是通过着色器GLSL ES语言 (opens new window)自定义材质效果,比如颜色。

  • .vertexShader:顶点着色器
  • .fragmentShader:片元着色器

顶点着色器vertexShader

顶点着色器属性vertexShader的值是字符串,字符串的内容是着色器GLSL ES语言写的代码。 为了方便预览顶点着色器代码,咱们用模板字符串``的形式去写 先按照着色器GLSL ES语言的语法,给顶点着色器代码设置一个主函数main,函数main无返回值,前面加上关键字void即可。

gl_Position关键字

gl_Position是着色器GLSL ES语言的内置变量,数据类型是四维向量vec4,前面三个参数表示xyz坐标,第四个参数一般为1.0。

const vertexShader = `
    void main(){
        gl_Position = vec4( x, y ,z ,1.0 );
    }
`
const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,// 顶点着色器
});

attribute关键字

attribute关键字一般用来声明与顶点数据有关变量。 attribute vec3 pos表示用attribute声明了一个变量pos,attribute的作用就是指明pos是顶点相关变量,pos的数类型是三维向量vec3,分别是x、y、z三个分量

const vertexShader = `
    attribute vec3 pos;
    void main(){
        gl_Position = vec4(pos,1.0 );
    }`

调用shader材质ShaderMaterial的时候,threejs默认插入了一行代码attribute vec3 position;,相当于帮你声明了一个变量position,position表示顶点的位置数据

const vertexShader = `
    // attribute vec3 position;//默认提供,不用自己写
    void main(){
        gl_Position = vec4(position,1.0 );
    }`

uniform关键字

用来声明非顶点的变量(顶点变量用atribute声明),比如模型的矩阵、光源位置等等。 执行uniform mat4 mT。意味着通过关键字uniform声明一个变量mT,变量mT的数据类型是mat4(4x4的矩阵)

调用shader材质ShaderMaterial的时候,threejs默认插入了一行代码uniform mat4 modelMatrix;。可以使用modelMatrix对几何体顶点位置坐标进行旋转、缩放、平移。

视图矩阵viewMatrix、投影矩阵projectionMatrix也是内置,通过viewMatrix和projectionMatrix来表示相机对场景模型的旋转、缩放、平移变换。

const vertexShader = `
    // uniform mat4 modelMatrix;//默认提供,不用自己写
    // uniform mat4 viewMatrix;//默认提供,不用自己写
    // uniform mat4 projectionMatrix;//默认提供,不用自己写
    void main(){
        // 投影矩阵 * 视图矩阵 * 模型矩阵 * 顶点坐标
        // 注意矩阵乘法前后顺序不要写错
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position,1.0 );
    }
`

片元着色器代码fragmentShader

gl_FragColor是着色器GLSL ES语言的内置变量。 gl_FragColor数据类型是四维向量vec4(r,g,b,a),第四个参数表示透明度,不透明就是1.0。

// 片元着色器代码
const fragmentShader = `
    void main() {
        // RGB 0.0,1.0,1.0对应16进制颜色是0x00ffff
        gl_FragColor = vec4(0.0,1.0,1.0,1.0);
    }`
const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,// 顶点着色器
    fragmentShader: fragmentShader,// 片元着色器
});

ShaderMaterial半透明、双面显示

side: THREE.DoubleSide设置双面显示

const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    side: THREE.DoubleSide // 双面显示
});

半透明通过片元着色器代码设置透明度,更改gl_FragColor的第四个分量。材质ShaderMaterial设置transparent:true,才会生效

// 片元着色器代码
const fragmentShader = `
    void main() {
        //透明度设置0.3,在0~1之间,半透明
        gl_FragColor = vec4(0.0,1.0,1.0,0.3);
    }`
const geometry = new THREE.PlaneGeometry(100, 50);
const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,// 顶点着色器
    fragmentShader: fragmentShader,// 片元着色器
    transparent:true  //开启透明
});

uniform声明变量

const fragmentShader = `
    uniform float opacity; //uniform声明透明度变量opacity
    uniform vec3 color; //声明一个颜色变量color
    void main() {
        gl_FragColor = vec4(color, opacity);
    }`

const material = new THREE.ShaderMaterial({
  uniforms: {
    // 给透明度uniform变量opacity传值
    opacity: {value:0.3}
    // 给uniform同名color变量传值
    color:{value:new THREE.Color(0x00ffff)}
  },
  vertexShader: vertexShader,
  fragmentShader: fragmentShader, // 片元着色器
  transparent: true, //允许透明
});

WebGL渲染管线执行

地址http://www.webgl3d.cn/pages/21c48e/ 顶点着色器到片元着色器的处理可成: 1、顶点缓冲区:顶点数据 2、顶点着色器:顶点变换 3、图元装配:比如渲染Mesh,Mesh几何体有多个三角形拼接,三个点为一组生成一个三角形 4、光栅化:比如在上一步三角形轮廓中,生成填充一个一个片元(像素) 5、片元着色器:给片元(像素)着色器

gl_FragCoord.xy片元屏幕坐标

gl_FragCoord.xy坐标系的坐标原点,位于threejs canvas画布的左下角,x轴水平向右,y轴竖直向上,单位是像素px。

根据片元屏幕坐标设置颜色

canvas画布总宽800px的情况下,以中间作为分界点,左半部分片元红色,右半部分片元蓝色。

if(gl_FragCoord.x < 400.0){
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}else {
  gl_FragColor = vec4(0.0,0.0,1.0,1.0);
}

根据片元x坐标,设置一个渐变色。

 gl_FragColor = vec4(gl_FragCoord.x/800.0*1.0,0.0,0.0,1.0);

discard是着色器语言GLSL ES的一个关键字,用来控制片元着色器功能单元,舍弃某个片元。

if(gl_FragCoord.x < 400.0){
  // 符合条件片元保留,并设置颜色
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}else {
  discard;//不符合条件片元直接舍弃掉
}

顶点颜色varying插值计算,顶点位置插值(实现渐变色)

ShaderMaterial和原来的网格材质一样,设置vertexColors:true,允许设置使用顶点颜色渲染

ShaderMaterial还有一个内置变量color,color变量表示插值之前的顶点颜色数据,varying关键字声明一个插值计算后的顶点颜色变量vColor。

// 顶点着色器代码
const vertexShader = `
// attribute vec3 color;//默认提供不用手写
varying vec3 vColor; //表示顶点插值后位置数据,与片元数量相同,一一对应
void main(){
  vColor = color;// 顶点颜色数据进行插值计算
  // 投影矩阵 * 模型视图矩阵 * 模型顶点坐标
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`

// 片元着色器代码
const fragmentShader = `
varying vec3 vColor; //获取顶点着色器插值数据vPosition
void main() {
    // gl_FragColor = vec4(0.0,1.0,1.0,1.0);
    gl_FragColor = vec4(vColor,1.0);
}`

颜色贴图.map

ShaderMaterial内置了顶点UV坐标变量uv(vec2),查看Uniform的文档,能看到着色器语言数据类型sampler2D

const vertexShader = `
    varying vec2 vUv;
    void main(){
        vUv = uv;// UV坐标插值计算
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }`

const fragmentShader = `
    uniform sampler2D map; //颜色贴图变量
    varying vec2 vUv;
    void main() {
        // 通过几何体的UV坐标从颜色贴图获取像素值
        gl_FragColor = texture2D( map, vUv );
    }`

const texture = new THREE.TextureLoader().load('./Earth.png');
const material = new THREE.ShaderMaterial({
  uniforms: {
    // 给着色器中同名uniform变量map传值
    map: {value: texture},
  },
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
});

空文件

简介

取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/programmer_luosx/3d-test1.git
git@gitee.com:programmer_luosx/3d-test1.git
programmer_luosx
3d-test1
3d-test1
master

搜索帮助