文章目录
- 顶点
- 片元
- 全部
核心:
顶点
varying vec3 vNormal;varying vec3 vViewPosition;void main() {vNormal = normalMatrix * normal;vNormal = normalize( vNormal );vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);gl_Position = projectionMatrix * modelViewPosition;vec3 transformed = vec3( position );vec4 mvPosition = vec4( transformed, 1.0 );mvPosition = modelViewMatrix * mvPosition;vViewPosition = - mvPosition.xyz;}
片元
varying vec3 vNormal; varying vec3 vViewPosition;uniform vec3 AmbientLight;uniform float roughness;#if NUM_SPOT_LIGHTS > 0struct SpotLight {vec3 position;vec3 direction;vec3 color;float distance;float decay;bool visible;float angle;float penumbra;float coneCos;float penumbraCos;};uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];#endif#include <common>float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {return smoothstep( coneCosine, penumbraCosine, angleCosine );}float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {// based upon Frostbite 3 Moving to Physically-based Rendering// page 32, equation 26: E[window1]// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdffloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );if ( cutoffDistance > 0.0 ) {distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );}return distanceFalloff;}void main() {gl_FragColor = vec4(vec3( 1,0,0 ) , 1. );#if NUM_SPOT_LIGHTS > 0#ifdef FLAT_SHADEDvec3 fdx = dFdx( vViewPosition );vec3 fdy = dFdy( vViewPosition );vec3 normal = normalize( cross( fdx, fdy ) );#elsevec3 normal = vNormal ;normal = normalize( vNormal );#endifvec3 geometryPosition = - vViewPosition;vec3 geometryNormal = normal;vec3 geometryViewDir = normalize( vViewPosition );SpotLight spotLight;vec3 directSpecular = vec3(0.);vec3 diffuse = vec3(0.);for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {spotLight = spotLights[ i ];vec3 lVector = spotLight.position - geometryPosition;vec3 lightDirection = normalize( lVector );// 漫反射float diff = max(dot(normal, lightDirection), 0.0);// * spotLight.colorfloat angleCos = dot( lightDirection, spotLight.direction);float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );// if(angleCos > spotLight.coneCos){// diffuse = vec3(1.);// }else {// diffuse = vec3(0.);// }if(spotAttenuation > 0.0){float lightDistance = length( lVector );float attenuation = getDistanceAttenuation(lightDistance, spotLight.distance, spotLight.decay);diffuse += diff * spotLight.color * spotAttenuation * attenuation;}}// gl_FragColor = vec4( directSpecular , 1. );gl_FragColor = vec4( diffuse + AmbientLight , 1. );#endif}
全部
import * as THREE from "three";
import { ThreeHelper } from "@/src/ThreeHelper";
import { MethodBaseSceneSet, LoadGLTF } from "@/src/ThreeHelper/decorators";
import { MainScreen } from "./Canvas";
import type { GLTF } from "three/examples/jsm/loaders/GLTFLoader";
import { Injectable } from "@/src/ThreeHelper/decorators/DI";
import type { GUI } from "dat.gui";
import { SpotLightMaterial } from "@/src/ThreeHelper/shader/material/SpotLight";
import { SpecularShaderMaterial } from "@/src/ThreeHelper/shader/material/Specular";@Injectable
// @ThreeHelper.useWebGPU
export class Main extends MainScreen {static instance: Main;// spotLight = new THREE.SpotLight(0xffffff, 100, 0, 1, 1, 2);spotLight = new THREE.SpotLight(0xffffff, 100, 0, 0.5, 1, 2);constructor(private helper: ThreeHelper) {super(helper);helper.main = this;this.init();Main.instance = this;}@MethodBaseSceneSet({addAxis: true,cameraPosition: new THREE.Vector3(-3, 2, 5),cameraTarget: new THREE.Vector3(0, 0, 0),useRoomLight: false,near: 0.1,far: 800,})init() {this.spotLight.position.set(0, 0, 2);// this.spotLight.target.position.set(0, 0, 0);this.helper.add(this.spotLight);// const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);// directionalLight.position.set(0, 0, 1);// const targetObject = new THREE.Object3D();// targetObject.position.set(0, 0, -1);// this.helper.scene.add(targetObject);// directionalLight.target = targetObject;// this.helper.scene.add(directionalLight);// const light = new THREE.AmbientLight(0x404040);// this.helper.scene.add(light);// {// const light = new THREE.PointLight(0xffffff, 10, 100);// light.position.set(0, 0, 3);// this.helper.scene.add(light);// }{const object = this.helper.create.plane(12, 30);object.mesh.position.y = -1;object.mesh.position.z = -5;object.mesh.rotateX(Math.PI / -2);object.material(new SpotLightMaterial({scene: this.helper.scene,camera: this.helper.camera,roughness: 0,})// new THREE.MeshStandardMaterial({ roughness: 0, metalness: 0 }));// const phongMaterial = new THREE.MeshPhongMaterial();// object.material(phongMaterial);this.helper.add(object.mesh);}const object = this.helper.create.sphere(1, 64, 64);object.material(new SpotLightMaterial({scene: this.helper.scene,camera: this.helper.camera,roughness: 0,})// new THREE.MeshStandardMaterial({ roughness: 0, metalness: 0 }));this.helper.add(object.mesh);{const object = this.helper.create.box(0.3, 0.3, 0.3);object.mesh.position.y = -1;object.mesh.position.x = -1;object.mesh.position.z = -2;object.mesh.rotateY(Math.PI / 4);object.material(new SpotLightMaterial({scene: this.helper.scene,camera: this.helper.camera,roughness: 0,})// new THREE.MeshStandardMaterial({ roughness: 0, metalness: 0 }));this.helper.add(object.mesh);}// plane.material(// new SpecularShaderMaterial({// scene: this.helper.scene,// camera: this.helper.camera,// roughness: 0.,// })// );// const phongMaterial = new THREE.MeshPhongMaterial();// object.material(phongMaterial);// phongMaterial.onBeforeCompile = (shader) => {// shader.fragmentShader = shader.fragmentShader.replace(// "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",// `// vec3 outgoingLight = reflectedLight.directDiffuse;// `// );// console.log(shader.fragmentShader);// };// plane.material(new THREE.MeshStandardMaterial({roughness:0,metalness:0}));// plane.material(new THREE.MeshBasicMaterial());}@LoadGLTF("/public/models/.glb")loadModel(gltf?: GLTF) {if (gltf) {this.helper.add(gltf.scene);}}@ThreeHelper.InjectAnimation(Main)animation() {// this.spotLight.target.rotation.x += 0.1}@ThreeHelper.AddGUI(Main)createEnvTexture(gui: GUI) {gui.add(this.spotLight, "visible").name("three.spotLight");const prev = this.spotLight.position.clone();const lightPosition = this.spotLight.position;const tempObj = new THREE.Object3D();this.helper.gui?.add({ v: 0 }, "v", -360, 360).onChange((v) => {lightPosition.copy(prev);tempObj.rotation.x = v * (Math.PI / 180);tempObj.updateMatrix();lightPosition.applyMatrix4(tempObj.matrix);// lightDirection.copy(lightPosition).multiplyScalar(-1).normalize();});gui.add(this.spotLight, "angle", 0, Math.PI / 3).onChange((val) => {this.spotLight.angle = val;});gui.add(this.spotLight, "penumbra", 0, 1).onChange((val) => {this.spotLight.penumbra = val;});gui.add(this.spotLight, "decay", 1, 4).onChange((val) => {this.spotLight.decay = val;});gui.add(this.spotLight, "intensity", 0, 100).onChange((val) => {this.spotLight.intensity = val;});gui.addFunction(() => {this.helper.scene.traverse((obj) => {const mesh = obj as THREE.Mesh<THREE.BufferGeometry, THREE.Material>;if (mesh.material) {if (mesh.material.type == "ShaderMaterial") {mesh.userData.material = mesh.material;mesh.material = new THREE.MeshPhongMaterial();} else {mesh.userData.material && (mesh.material = mesh.userData.material);}}});}, "Toggle Material");}
}
import * as THREE from "three";interface SpotLightUniforms {color: THREE.SpotLight["color"];intensity: THREE.SpotLight["intensity"];distance: THREE.SpotLight["distance"];decay: THREE.SpotLight["decay"];visible: THREE.SpotLight["visible"];position: THREE.SpotLight["position"];coneCos: number;penumbraCos: number;
}export class SpotLightMaterial extends THREE.ShaderMaterial {Lights: SpotLightUniforms[] = [];original: ConstructorParameters<typeof THREE.ShaderMaterial>[0];color = new THREE.Color(0xffffff);onBeforeRender(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera): void {const Lights: SpotLightUniforms[] = [];const viewMatrix = camera.matrixWorldInverse;const vector3 = new THREE.Vector3();scene.traverse((obj) => {if (obj.type == "SpotLight") {const light = obj as THREE.SpotLight;const uniforms = {position: new THREE.Vector3(),direction: new THREE.Vector3(),color: light.color.clone().multiplyScalar(light.intensity),intensity: light.intensity,distance: light.distance,decay: light.decay,visible: light.visible,coneCos: Math.cos(light.angle),penumbraCos: Math.cos(light.angle * (1 - light.penumbra)),};uniforms.position.setFromMatrixPosition(light.matrixWorld);uniforms.position.applyMatrix4(viewMatrix);uniforms.direction.setFromMatrixPosition(light.matrixWorld);vector3.setFromMatrixPosition(light.target.matrixWorld);uniforms.direction.sub(vector3);uniforms.direction.transformDirection(viewMatrix);// console.log(uniforms.position)// console.log(uniforms.direction)Lights.push(uniforms);}});// this.fragmentShader = this.replaceLightNums(this.original!.fragmentShader!, {// numPointLights: PointLights.length,// });this.uniforms.spotLights.value = Lights;}constructor(params?: ConstructorParameters<typeof THREE.ShaderMaterial>[0] & {scene: THREE.Scene;camera: THREE.PerspectiveCamera;roughness: number;AmbientLight?: THREE.Color;}) {const original = {uniforms: {spotLights: {value: [] as SpotLightUniforms[],},roughness: {value: params?.roughness,},AmbientLight: { value: params?.AmbientLight ?? new THREE.Color(0x000000) },},defines: {RECIPROCAL_PI: 1 / Math.PI,// 平直着色 关闭则 平滑着色FLAT_SHADED: false,},vertexShader: /* glsl */ `varying vec3 vNormal;varying vec3 vViewPosition;void main() {vNormal = normalMatrix * normal;vNormal = normalize( vNormal );vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);gl_Position = projectionMatrix * modelViewPosition;vec3 transformed = vec3( position );vec4 mvPosition = vec4( transformed, 1.0 );mvPosition = modelViewMatrix * mvPosition;vViewPosition = - mvPosition.xyz;}`,fragmentShader: /* glsl */ `varying vec3 vNormal; varying vec3 vViewPosition;uniform vec3 AmbientLight;uniform float roughness;#if NUM_SPOT_LIGHTS > 0struct SpotLight {vec3 position;vec3 direction;vec3 color;float distance;float decay;bool visible;float angle;float penumbra;float coneCos;float penumbraCos;};uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];#endif#include <common>float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {return smoothstep( coneCosine, penumbraCosine, angleCosine );}float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {// based upon Frostbite 3 Moving to Physically-based Rendering// page 32, equation 26: E[window1]// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdffloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );if ( cutoffDistance > 0.0 ) {distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );}return distanceFalloff;}void main() {gl_FragColor = vec4(vec3( 1,0,0 ) , 1. );#if NUM_SPOT_LIGHTS > 0#ifdef FLAT_SHADEDvec3 fdx = dFdx( vViewPosition );vec3 fdy = dFdy( vViewPosition );vec3 normal = normalize( cross( fdx, fdy ) );#elsevec3 normal = vNormal ;normal = normalize( vNormal );#endifvec3 geometryPosition = - vViewPosition;vec3 geometryNormal = normal;vec3 geometryViewDir = normalize( vViewPosition );SpotLight spotLight;vec3 directSpecular = vec3(0.);vec3 diffuse = vec3(0.);for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {spotLight = spotLights[ i ];vec3 lVector = spotLight.position - geometryPosition;vec3 lightDirection = normalize( lVector );// 漫反射float diff = max(dot(normal, lightDirection), 0.0);// * spotLight.colorfloat angleCos = dot( lightDirection, spotLight.direction);float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );// if(angleCos > spotLight.coneCos){// diffuse = vec3(1.);// }else {// diffuse = vec3(0.);// }if(spotAttenuation > 0.0){float lightDistance = length( lVector );float attenuation = getDistanceAttenuation(lightDistance, spotLight.distance, spotLight.decay);diffuse += diff * spotLight.color * spotAttenuation * attenuation;}}// gl_FragColor = vec4( directSpecular , 1. );gl_FragColor = vec4( diffuse + AmbientLight , 1. );#endif}`,};const SpotLights: SpotLightUniforms[] = [];const viewMatrix = params!.camera.matrixWorldInverse;const vector3 = new THREE.Vector3();params!.scene.traverse((obj) => {if (obj.type == "SpotLight") {const light = obj as THREE.SpotLight;const uniforms = {position: new THREE.Vector3(),direction: new THREE.Vector3(),color: light.color.clone().multiplyScalar(light.intensity),intensity: light.intensity,distance: light.distance,decay: light.decay,visible: light.visible,coneCos: Math.cos(light.angle),penumbraCos: Math.cos(light.angle * (1 - light.penumbra)),};console.log(uniforms.coneCos);console.log(uniforms.penumbraCos);uniforms.position.setFromMatrixPosition(light.matrixWorld);uniforms.position.applyMatrix4(viewMatrix);uniforms.direction.setFromMatrixPosition(light.matrixWorld);vector3.setFromMatrixPosition(light.target.matrixWorld);uniforms.direction.sub(vector3);uniforms.direction.transformDirection(viewMatrix);SpotLights.push(uniforms);}});const replaceLightNums = (string: string, parameters: { numSpotLights: number }) => {return string.replace(/NUM_SPOT_LIGHTS/g, "" + parameters.numSpotLights);};original.uniforms.spotLights = { value: SpotLights };original.fragmentShader = replaceLightNums(original!.fragmentShader!, {numSpotLights: SpotLights.length,});super(original);this.original = original;}
}