本文章不做模型讲解,只分享代码。
在线地址 https://codepen.io/qinwei-liao/pen/BaqVegP
index.html
<canvas id="renderer"></canvas>
<script type="importmap">
{
"imports":{
"three":"https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.min.js"
}
}
</script>
<script src="./index.js" type="module"></script>
index.js
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.min.js";
import {OrbitControls} from "https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/controls/OrbitControls.js"
import GUI from "https://cdn.jsdelivr.net/npm/lil-gui@0.18.1/dist/lil-gui.esm.min.js"
const gui = new GUI;
const vertexShader = `
precision highp float;
uniform vec3 lightPosition;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 normal;
varying vec3 vNormal;
void main() {
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
}
`
// 模拟的是方向光,没有计算光源的衰减
const fragmentShader = `
precision highp float;
uniform vec3 lightDirection;
uniform vec3 lightColor;
uniform float kd;
uniform bool halfLambert;
varying vec3 vNormal;
void main() {
vec3 color = vec3(.5, .5, 0);
vec3 diffuse = kd * color * lightColor * (clamp(dot(vNormal, lightDirection), 0., 1.));
if(halfLambert) {
diffuse = kd * color * lightColor * (clamp(dot(vNormal, lightDirection) * .5, 0., 1.) + .5);
}
gl_FragColor = vec4(diffuse, 1);
}
`
const canvas = document.querySelector("#renderer")
const renderer = new THREE.WebGLRenderer({ canvas, logarithmicDepthBuffer: false });
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 4;
const controls = new OrbitControls(camera, canvas)
const scene = new THREE.Scene();
const intensity = 2;
const color = 0xFFFFFF;
const light = new THREE.DirectionalLight(color, intensity);
scene.add(light);
light.rotation.set(0,1.3,-1.1)
const uniforms = {
lightDirection:{value:light.getWorldDirection(new THREE.Vector3)},
lightColor:{value:light.color},
kd:{value:1},
halfLambert:{value:false}
}
const geometry = new THREE.TorusKnotGeometry(1, .5, 100, 16)
const material = new THREE.RawShaderMaterial({
uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
})
const change = (v)=>{
material.uniforms.lightDirection.value = light.getWorldDirection(new THREE.Vector3)
}
gui.add(light.rotation,"x").step(.1).onChange(change)
gui.add(light.rotation,"y").step(.1).onChange(change)
gui.add(light.rotation,"z").step(.1).onChange(change)
gui.add({kd:1},"kd",0,1,.1).onChange((v)=>{
material.uniforms.kd.value = v
})
gui.add({halfLambert:false},"halfLambert").onChange(v=>{
material.uniforms.halfLambert.value = v
})
scene.add(new THREE.Mesh(geometry, material))
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const needResize = canvas.width !== window.innerWidth || canvas.height !== window.innerHeight;
if (needResize) {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
renderer.setSize(window.innerWidth, window.innerHeight, false);
}
return needResize;
}
let then = 0;
function render(now) {
now *= 0.001; // convert to
const deltaTime = now - then;
then = now;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera)
requestAnimationFrame(render);
}
requestAnimationFrame(render);