着色器材质可以自定义几何体面的颜色。进一步学习后就会知道,three.js
就是对GLSL
语言进行了多方面的封装,下面我们就使用着色器语言来绘制一个正方体。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>学习</title>
</head>
<body>
<canvas id="c2d" class="c2d" width="1000" height="500"></canvas>
<script type="module">
import * as THREE from './file/three.js-dev/build/three.module.js'
import { OrbitControls } from './file/three.js-dev/examples/jsm/controls/OrbitControls.js'
const canvas = document.querySelector('#c2d')
// 渲染器
const renderer = new THREE.WebGLRenderer({ canvas })
const fov = 40 // 视野范围
const aspect = 2 // 相机默认值 画布的宽高比
const near = 0.1 // 近平面
const far = 10000 // 远平面
// 透视投影相机
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0, 0, 3)
camera.lookAt(0, 0, 0)
// 控制相机
const controls = new OrbitControls(camera, canvas)
controls.update()
// 场景
const scene = new THREE.Scene()
// 顶点着色器
const vertexShader = `
varying vec2 vUv;
varying vec3 vPosition;
void main(){
gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
vUv=uv;
vPosition=position;
}
`
// 片元着色器
const fragmentShader = `
uniform float uTime;
varying vec2 vUv;
vec3 background(vec2 uv){
float dist=length(uv-vec2(.5));
vec3 bg=mix(vec3(.3),vec3(.0),dist);
return bg;
}
void main(){
vec3 bg=background(vUv);
vec3 color=bg;
gl_FragColor=vec4(color,1.);
}
`
const geometry = new THREE.BoxGeometry(1, 1, 1)
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0
}
}
})
const mesh = new THREE.Mesh(geometry, shaderMaterial)
scene.add(mesh)
// 渲染
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
</script>
</body>
</html>
projectionMatrix
是投影变换矩阵,modelViewMatrix
是相机坐标系的变换矩阵,position
顶点坐标,都是three.js
定义好的。background
函数计算出当前顶点的颜色,交由片元自动计算各个顶点之间的渐变。 float sdBox(vec3 p,vec3 b){
vec3 q=abs(p)-b;
return length(max(q,0.))+min(max(q.x,max(q.y,q.z)),0.);
}
float rayMarch(vec3 ro,vec3 rd,float end,int maxIter){
float d0 = 0.;
for(int i=0;i<maxIter;i++){
vec3 pos=ro+d0*rd;
float ds=sdBox(pos,vec3(.3));
d0+=ds;
if(ds >= end || ds < 0.01){
break;
}
}
return d0;
}
void main(){
...
// 光线位置 位置
vec3 eye = vec3(0.,0.,2.5);
// 方向
vec3 ray = normalize(vec3(vUv,-eye.z));
// 最大距离
float end = 5.;
// 最大步数
int maxIter=256;
float depth = rayMarch(eye,ray,end,maxIter);
if(depth < end){
vec3 pos = eye + depth * ray;
color = pos;
}
...
}
uTime
全局变量,通过修改它来实现旋转方块。 mat4 rotationMatrix(vec3 axis,float angle){
axis=normalize(axis);
float s=sin(angle);
float c=cos(angle);
float oc=1.-c;
return mat4(oc*axis.x*axis.x+c,oc*axis.x*axis.y-axis.z*s,oc*axis.z*axis.x+axis.y*s,0.,
oc*axis.x*axis.y+axis.z*s,oc*axis.y*axis.y+c,oc*axis.y*axis.z-axis.x*s,0.,
oc*axis.z*axis.x-axis.y*s,oc*axis.y*axis.z+axis.x*s,oc*axis.z*axis.z+c,0.,
0.,0.,0.,1.);
}
vec3 rotate(vec3 v,vec3 axis,float angle){
mat4 m=rotationMatrix(axis,angle);
return(m*vec4(v,1.)).xyz;
}
rayMarch
函数,在加载方块之前先旋转方块。这里要注意rotate()
函数需要在rayMarch()
函数之前加载。float rayMarch(vec3 ro,vec3 rd,float end,int maxIter){
...
vec3 pos=ro+d0*rd;
vec3 p1=rotate(pos,vec3(1.),uTime);
float ds=sdBox(p1,vec3(.3));
...
}
vec2 centerUv(vec2 uv){
uv=2.*uv-1.;
float aspect=1;
uv.x*=aspect;
return uv;
}
...
// 方向
vec2 cUv=centerUv(vUv);
vec3 ray=normalize(vec3(cUv,-eye.z));
// vec3 ray = normalize(vec3(vUv,-eye.z));
...
GLSL语言
的简单应用,通过各种公式使用着色器来绘制立方体。three.js
的源码也是在控制着色器,所以想要深入理解three.js
就必须要学会如何使用GLSL语言
。