本文章不做模型讲解,只分享代码。
在线地址 https://codepen.io/qinwei-liao/pen/MWPBKVr
index.html
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize-css@2.3.1/normalize.min.css">
</head>
<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 fragmentShader = `
precision highp float;
uniform vec3 lightPosition;
uniform float lightIntensity;
uniform vec3 lightColor;
uniform vec3 cameraPosition;
uniform float kd;
uniform float ka;
uniform float ks;
uniform bool isPhong;
varying vec3 vNormal;
varying vec3 vWorldPosition;
void main() {
vec3 color = vec3(1, 1.0, 0);
float r = distance(vWorldPosition, lightPosition);
float radiance = lightIntensity / (r * r);
vec3 l = lightPosition - vWorldPosition;
vec3 ambient = ka * vec3(1, 1, 1) * color;
vec3 diffuse = color * lightColor * kd * radiance * max(dot(vNormal, normalize(l)), 0.);
vec3 h = normalize(l + (cameraPosition - vWorldPosition));
float specularAngle = dot(h, vNormal);// blinn-phong
if(isPhong)
specularAngle = dot(normalize(2. * dot(l, vNormal) * vNormal - l), normalize(cameraPosition - vWorldPosition));
vec3 specular = lightColor * ks * radiance * pow(max(0., specularAngle), 64.);
gl_FragColor = vec4(ambient + diffuse + specular, 1);
}
`
const vertexShader = `
precision highp float;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 normal;
varying vec3 vNormal;
varying vec3 vWorldPosition;
void main() {
vNormal = normal;
vWorldPosition = (modelMatrix * vec4(position, 1.)).xyz;
gl_Position = projectionMatrix * viewMatrix * vec4(vWorldPosition, 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 pointLight = new THREE.PointLight(new THREE.Color(0xFFFFFF), 1.);
pointLight.position.set(0, 0, 1.5)
scene.add(pointLight)
let ka = .1, kd = .6, ks = .9
const uniforms = {
lightPosition: { value: pointLight.getWorldPosition(new THREE.Vector3) },
lightIntensity: { value: pointLight.intensity },
lightColor: { value: pointLight.color },
kd: { value: kd },
ka: { value: ka },
ks: { value: ks },
isPhong:{value:false}
}
const geometry = new THREE.TorusKnotGeometry(1, .5, 100, 1600)
const material = new THREE.RawShaderMaterial({
uniforms,
vertexShader,
fragmentShader
})
const phong = gui.addFolder("phong")
phong.add({ kd: kd }, "kd", 0, 1, .1).onChange((v) => {
material.uniforms.kd.value = v
})
phong.add({ ka: ka }, "ka", 0, 1, .1).onChange((v) => {
material.uniforms.ka.value = v
})
phong.add({ ks: ks }, "ks", 0, 1, .1).onChange((v) => {
material.uniforms.ks.value = v
})
phong.add({isPhong:false},"isPhong").onChange(v=>{
material.uniforms.isPhong.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 seconds
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);