前言
补充:\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 = 1
child.material.needsUpdate = true
child.castShadow = true
child.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.sRGBEncoding
scene.background = environmentMap
scene.environment = environmentMap
/**
* Material
*/
// Textures
const mapTexture = textureLoader.load('/models/LeePerrySmith/color.jpg')
mapTexture.encoding = THREE.sRGBEncoding
const 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.uTime
shader.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
// 在外部创建uTime
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;
`
)
}
depthMaterial.onBeforeCompile = (shader) =>{ // 自定义深度材质
shader.uniforms.uTime = customUniforms.uTime
shader.uniforms.uSpeed = customUniforms.uSpeed
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));
}
`
)
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) =>
{
// Model
const mesh = gltf.scene.children[0]
mesh.rotation.y = Math.PI * 0.5
mesh.material = material
mesh.customDepthMaterial = depthMaterial
scene.add(mesh)
// Update materials
updateAllMaterials()
}
)
/*
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 sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.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 material
customUniforms.uTime.value = elapsedTime
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
二、效果
对应代码可以自己修改,对应视频中的样式,上面代码属于完全版本的,一些实现的难点可以自己去试错
shader- 人物模型 扭曲 旋转
shader- 人物模型 扭曲 旋转阴影问题
shader- 人物模型 扭曲 旋转
总结
我们可以加入一些变量控制gui,实现扭曲的变化!