前言
补充:\node_modules\three\src\renderers\shaders
自Three.js第132版以来,位于ShaderLib/文件夹中的着色器现在按材质分组。
顶点代码和片段代码都在同一个文件中。
课程
学习如何改进3DS内置材质
改进网格标准材质
两种方法
1.使用Three.js钩子,让我们调整着色器并注入代码(在编译之前将我们的代码注入到材料中)。 (使用这种方式)
2.通过重新创建材料,但遵循Three.js代码中所做的工作。
怎么做?
要挂钩材料,先找到材料
1.使用onBeforeCompile() 这是一个空函数 ,在材料编译之前,3GS回自动调用此函数
可以使用着色器,使用此处的任何参数访问其中的内容
2.地址\node_modules\three\src\renderers\shaders\ShaderLib\
中关于材质部分代码 meshphysical.glsl #include <begin_vertex>
在字符中注入代码
3.扭曲模型,根据海拔高度进行不同的旋转
https://thebookofshaders.com/08/
4.人物旋转 ,会发现阴影有问题
5. uniforms制服 (公共数据) 添加uTime旋转
6.修复阴影
为什么阴影有问题,因为设置阴影贴图是深度材质(深度材质没有阴影),并没有扭曲
投射阴影没有变化
法线没有变化,法线告诉我们什么是外部
模型阴影没有变化
7. 需要创建自己的网络深度材质
MeshDepthMaterial
之后和设置material一样设置自定义深度材质,注入自己的代码,让阴影扭曲
投影阴影好了,模型阴影还没
8. 需要旋转法线
\node_modules\three\src\renderers\shaders\ShaderChunk
beginnormal_vertex.glsl.js
两个变量 objectNormal ,objectTangent
这里注意 node_modules里面的shader文件,对应代码我们通过下面方式实现代码的注入。
一、代码
学习如何改进3DS内置材质,shader ,这里放到一个文件下,也可以和之前的一样分出去。
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import * as dat from 'lil-gui'/*** Base*/
// Debug
const gui = new dat.GUI()// Canvas
const canvas = document.querySelector('canvas.webgl')// Scene
const scene = new THREE.Scene()/*** Loaders*/
const textureLoader = new THREE.TextureLoader()
const gltfLoader = new GLTFLoader()
const cubeTextureLoader = new THREE.CubeTextureLoader()/*** Update all materials*/
const updateAllMaterials = () =>
{scene.traverse((child) =>{if(child instanceof THREE.Mesh && child.material instanceof THREE.MeshStandardMaterial){child.material.envMapIntensity = 1child.material.needsUpdate = truechild.castShadow = truechild.receiveShadow = true}})
}/*** Environment map*/
const environmentMap = cubeTextureLoader.load(['/textures/environmentMaps/0/px.jpg','/textures/environmentMaps/0/nx.jpg','/textures/environmentMaps/0/py.jpg','/textures/environmentMaps/0/ny.jpg','/textures/environmentMaps/0/pz.jpg','/textures/environmentMaps/0/nz.jpg'
])
environmentMap.encoding = THREE.sRGBEncodingscene.background = environmentMap
scene.environment = environmentMap/*** Material*/// Textures
const mapTexture = textureLoader.load('/models/LeePerrySmith/color.jpg')
mapTexture.encoding = THREE.sRGBEncodingconst normalTexture = textureLoader.load('/models/LeePerrySmith/normal.jpg')// Material
const material = new THREE.MeshStandardMaterial( {map: mapTexture,normalMap: normalTexture
})// 创建自己的网络深度材质
const depthMaterial = new THREE.MeshDepthMaterial({depthPacking: THREE.RGBADepthPacking //depth packing的编码。
})const customUniforms = {uTime:{value:0},uSpeed:{value:0.2},
}material.onBeforeCompile = (shader) =>{ // 材质加载之前调用console.log(shader)shader.uniforms.uTime = customUniforms.uTimeshader.uniforms.uSpeed = customUniforms.uSpeed// 这里将node_modules中关于材质部分着色器代码替换// 只是现在我们可以添加自己的代码更改转换后的变量shader.vertexShader = shader.vertexShader.replace( // common 中添加了扭曲函数函数'#include <common>',` #include <common> uniform float uTime;uniform float uSpeed;mat2 get2dRotateMatrix(float _angle){return mat2(cos(_angle),-sin(_angle),sin(_angle),cos(_angle));}`)// normal 法线shader.vertexShader = shader.vertexShader.replace( // common 中添加了扭曲函数函数'#include <beginnormal_vertex>',` #include <beginnormal_vertex>float angle = sin(position.y + uTime) * uSpeed ;mat2 rotateMatrix = get2dRotateMatrix(angle);objectNormal.xz = rotateMatrix * objectNormal.xz;`)// 执行扭曲函数,transformed进行旋转, (阴影有问题)// 加入uTime ,跟随时间变化旋转// 加入但是没有动画 在tick()中也要加入,但是没办法访问uTime// 在外部创建uTimeshader.vertexShader = shader.vertexShader.replace( '#include <begin_vertex>',` #include <begin_vertex> // float angle = sin(position.y + uTime) * uSpeed ; // 这里注释调,因为在上一步中已经旋转,同样在同一个函数中不能重复定义相同变量// mat2 rotateMatrix = get2dRotateMatrix(angle);transformed.xz = rotateMatrix * transformed.xz;`)}depthMaterial.onBeforeCompile = (shader) =>{ // 自定义深度材质shader.uniforms.uTime = customUniforms.uTimeshader.uniforms.uSpeed = customUniforms.uSpeedshader.vertexShader = shader.vertexShader.replace( // common 中添加了扭曲函数函数'#include <common>',` #include <common> uniform float uTime;uniform float uSpeed;mat2 get2dRotateMatrix(float _angle){return mat2(cos(_angle),-sin(_angle),sin(_angle),cos(_angle));}`)shader.vertexShader = shader.vertexShader.replace( '#include <begin_vertex>',` #include <begin_vertex> float angle = sin(position.y + uTime) * uSpeed ;mat2 rotateMatrix = get2dRotateMatrix(angle);transformed.xz = rotateMatrix * transformed.xz;`)
}gui.add(customUniforms.uSpeed,'value').min(0).max(1).step(0.01).name('速度')/*** Models*/
gltfLoader.load('/models/LeePerrySmith/LeePerrySmith.glb',(gltf) =>{// Modelconst mesh = gltf.scene.children[0]mesh.rotation.y = Math.PI * 0.5mesh.material = materialmesh.customDepthMaterial = depthMaterialscene.add(mesh)// Update materialsupdateAllMaterials()}
)/* plan
*/
const plan = new THREE.Mesh(new THREE.PlaneBufferGeometry(15,15,15),new THREE.MeshStandardMaterial({})
)
plan.rotation.y = Math.PI
plan.position.y = -5
plan.position.z = 5
scene.add(plan)/*** Lights*/
const directionalLight = new THREE.DirectionalLight('#ffffff', 3)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.set(1024, 1024)
directionalLight.shadow.camera.far = 15
directionalLight.shadow.normalBias = 0.05
directionalLight.position.set(0.25, 2, - 2.25)
scene.add(directionalLight)/*** Sizes*/
const sizes = {width: window.innerWidth,height: window.innerHeight
}window.addEventListener('resize', () =>
{// Update sizessizes.width = window.innerWidthsizes.height = window.innerHeight// Update cameracamera.aspect = sizes.width / sizes.heightcamera.updateProjectionMatrix()// Update rendererrenderer.setSize(sizes.width, sizes.height)renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})/*** Camera*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.set(4, 1, - 4)
scene.add(camera)// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true/*** Renderer*/
const renderer = new THREE.WebGLRenderer({canvas: canvas,antialias: true
})
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFShadowMap
renderer.physicallyCorrectLights = true
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 1
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))/*** Animate*/
const clock = new THREE.Clock()const tick = () =>
{const elapsedTime = clock.getElapsedTime()// update materialcustomUniforms.uTime.value = elapsedTime// Update controlscontrols.update()// Renderrenderer.render(scene, camera)// Call tick again on the next framewindow.requestAnimationFrame(tick)
}tick()
二、效果
对应代码可以自己修改,对应视频中的样式,上面代码属于完全版本的,一些实现的难点可以自己去试错
shader- 人物模型 扭曲 旋转
shader- 人物模型 扭曲 旋转阴影问题
shader- 人物模型 扭曲 旋转
总结
我们可以加入一些变量控制gui,实现扭曲的变化!