【Three.js基础学习】27.Modified materials

embedded/2024/11/22 1:29:32/


前言

  补充:\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,实现扭曲的变化!


http://www.ppmy.cn/embedded/139479.html

相关文章

用匠心精神解决LeetCode第726题原子的数量

726.原子的数量 难度&#xff1a;困难 问题描述&#xff1a; 给你一个字符串化学式formula&#xff0c;返回每种原子的数量。 原子总是以一个大写字母开始&#xff0c;接着跟随0个或任意个小写字母&#xff0c;表示原子的名字。 如果数量大于1&#xff0c;原子后会跟着数字…

云计算虚拟化-kvm创建虚拟机

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 虚拟化&#xff0c;简单来说就是把一台服务器/PC电脑&#xff0c;虚拟成多台独立的虚拟机&#xff0c;每台虚拟机之间相互隔…

【大语言模型】ACL2024论文-12 大型语言模型的能力如何受到监督式微调数据组成影响

【大语言模型】ACL2024论文-12 大型语言模型的能力如何受到监督式微调数据组成影响 论文&#xff1a;https://arxiv.org/pdf/2310.05492 目录 文章目录 【大语言模型】ACL2024论文-12 大型语言模型的能力如何受到监督式微调数据组成影响论文&#xff1a;https://arxiv.org/p…

sql中的聚合函数

SQL中的聚合函数用于对表中的数据进行汇总计算&#xff0c;常用来生成统计信息&#xff0c;例如总和、平均值、最大值、最小值等。它们通常与GROUP BY子句一起使用&#xff0c;以对数据分组后再计算聚合结果。 以下是SQL中常用的聚合函数及其详细讲解&#xff1a; 1. COUNT( )…

STM32编程遇到的问题随笔【一】

STM32编程遇到的问题随笔【一】 一、PB4引脚输出一直为高&#xff0c;无论怎么拉低都不起作用 原因PB4和PB3是复用引脚&#xff0c;用于JTAG调试&#xff0c;芯片是默认开启JTAG功能的&#xff0c;如果我们需要用到这两个引脚&#xff0c;必须降JTAG调试功能关闭&#xff0c;…

C语言和C++的不同

C语言和C都是非常重要的编程语言&#xff0c;它们有着紧密的联系&#xff0c;但也存在显著的差异。以下是对C语言和C的一些主要异同的分析&#xff0c;以及对常用语句的对比。 1. 基本概念与用途 C语言&#xff1a;C语言是一种过程式编程语言&#xff0c;它提供了对低级内存操…

Ubuntu问题 - 显示ubuntu服务器上可用磁盘空间 一条命令df -h

目的 想要放我的 数据集 到新的ubuntu服务器中, 不知道存储空间够不够 开始 使用以下命令直接查看 df -h

【RK3588 Linux 5.x 内核编程】-内核线程

内核线程 文章目录 内核线程1、进程与线程介绍2、线程管理3、内核线程管理函数3.1 创建内核线程3.2 启动内核线程3.3 停止内核线程4、内核线程示例实现4.1 内核线程函数定义4.2 创建和启动内核线程4.3 停止内核线程4.4 完整示例代码5、驱动验证线程是并发处理中使用的编程抽象。…