目 录CONTENT

文章目录

ThreeJS框架材质原理与应用

Hello!你好!我是村望~!
2022-08-12 / 0 评论 / 0 点赞 / 293 阅读 / 859 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

引言

学过一些Three.js内置的着色器,也通过原始着色器材质(RawShaderMaterial)+自己编写着色器代码,来自定义一些材质!

那么当我们使用一些内置着色器的时候,那么我们能不能通过一些方法,让我们可以看到他内部着色器是如何实现的,甚至说可以在内置的基础上我们对其效果进一步加工!不必让内置的困住我们,也不必自定义的时候从头到尾自己去写一个新的着色器材质代码!

onBeforeCompile

在编译shader程序之前立即执行的可选回调。此函数使用shader源码作为参数。用于修改内置材质。
和其他属性不一样的是,这个回调在.clone(),.copy() 和 .toJSON() 中不支持。

首先我们准备一个内置的标准材质的代码!

import * as THREE from "three"
import {
    OrbitControls
} from "three/examples/jsm/controls/OrbitControls"
import {
    handleResize
} from "@/common/utils"
// 场景
const scene = new THREE.Scene()
const spotLight = new THREE.SpotLight(0xffffff, 1)
scene.add(spotLight)
spotLight.position.x = 0
spotLight.position.y = 250
spotLight.position.z = 350
spotLight.shadow.mapSize.set(2048, 2048)
// 相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000)
camera.position.set(550, 550, 550)

// 坐标辅助
const axes = new THREE.AxesHelper(100)
scene.add(axes)

// 一个几何球模型
const sphereRadius = 20
const sphereGeometry = new THREE.SphereBufferGeometry(sphereRadius, 200, 200)
const sphereMaterial = new THREE.MeshStandardMaterial({
    color: 0xFDF860,
    roughness: 0,

})
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial)
sphereMesh.position.y = sphereRadius
scene.add(sphereMesh)
sphereMesh.castShadow = true

// 一个平面
const planeGeometry = new THREE.PlaneBufferGeometry(500, 500, 100, 100)
const planeMaterial = new THREE.MeshStandardMaterial({
    color: 0x000000,
    roughness: 0,
    side: THREE.DoubleSide,
    color: 0xfffff0
})
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
planeMesh.rotation.x = -Math.PI / 2
planeMesh.receiveShadow = true
scene.add(planeMesh)

// 渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

// 控制器
const controller = new OrbitControls(camera, renderer.domElement)
// renderer.setClearAlpha(0)
spotLight.castShadow = true
renderer.shadowMap.enabled = true
handleResize(camera, renderer)
// 渲染函数
const handleRender = () => {
    controller.update
    renderer.render(scene, camera)
    requestAnimationFrame(handleRender)
}
requestAnimationFrame(handleRender)

1-1660291446374
我们尝试一下这个API,输出一下,看看打印什么?

const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
planeMaterial.onBeforeCompile = ((shader,render)=>{
    console.log('====================================');
    console.log(shader);
    console.log(render);
    console.log('====================================');
})

image-1660292008444
image-1660292039553
可以看到shader中我们熟悉的顶点着色器和片元着色器,还有uniforms

尝试修改一下内置着色器的代码

通过打印我们可以看到着色器的代码都是字符串的!所以想加点什么,改点什么,可以通过replace的方式

还有需要注意的地方!我们如果要新增自定义的uniforms的话,是不能直接通过材质的uniforms访问的!(不信可以试一下哈哈哈哈)因此最好设置一个全局的引用类型的变量,每次动画帧更新的时候,更新引用对象value就好了!

还有,保证格式对的情况下,尽量减少空格!

下面是一个案例,修改了顶点着色器和uniform

// 自定义 uniforms
const customUniform = {
    uTime:{
        value:0
    }
}


planeMaterial.onBeforeCompile = ((shader,render)=>{
    shader.uniforms.uTime = customUniform.uTime;
    shader.vertexShader = shader.vertexShader.replace(
    `#include <common>`,
    `uniform float uTime;
    #include <common>`
)
    shader.vertexShader = shader.vertexShader.replace(
        `#include <begin_vertex>`,
        `#include <begin_vertex>
         transformed.x+=200.0*(sin(uTime));transformed.y+=200.0*(sin(uTime));
        `
    )
})

...

// 渲染函数
const handleRender = (time) => {
    renderer.render(scene, camera)
    customUniform.uTime.value = clock.getElapsedTime() // 更新uniform的引用对象!
    controller.update
    requestAnimationFrame(handleRender)
}
requestAnimationFrame(handleRender)

最终效果!
1-1660300075811

0

评论区