问题描述
在我的three.js场景中,我有一个加载了THREE.OBJLoader的.obj文件。
我想在保留对象MeshStandardMaterial着色器的同时,在z轴上将线性颜色渐变应用于此对象。下面是在对象上应用2色线性渐变的示例。
在保留MeshStandardMaterial类随附的着色器的同时,我需要这样做。我想做到这一点,我需要用一些代码注入MeshStandardMaterial的vertexShader和materialShaders。
此question的答案部分实现了这一点。这些答案将创建一种新的着色器材质,而没有我需要的MeshStandardMaterial属性。
我尝试使用onBeforeCompile,但不知道将上面提到的question的答案中概述的代码注入哪里。我对着色器只有一个基本的了解。
我的对象的材质由THREE.MeshStandardMaterial类定义。我设置了该类的以下属性:金属性,粗糙度,透明性和不透明性。我用纹理设置了map和envmap属性。
解决方法
很难理解着色器块中的内容是什么,但并非不可能。
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js";
import {OrbitControls} from "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60,innerWidth / innerHeight,1,1000);
camera.position.set(0,10,20);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth,innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera,renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff,1);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff,1));
let g = new THREE.TorusKnotBufferGeometry(5,128,16);
g.rotateX(-Math.PI * 0.5);
g.computeBoundingBox();
let uniforms = {
bbMin: {value: g.boundingBox.min},bbMax: {value: g.boundingBox.max},color1: {value: new THREE.Color(0xff0000)},color2: {value: new THREE.Color(0xffff00)}
}
console.log(g);
let m = new THREE.MeshStandardMaterial({
roughness: 0.25,metalness: 0.75,map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/floors/FloorsCheckerboard_S_Diffuse.jpg",tex => {
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set( 16,1 );
}),onBeforeCompile: shader => {
shader.uniforms.bbMin = uniforms.bbMin;
shader.uniforms.bbMax = uniforms.bbMax;
shader.uniforms.color1 = uniforms.color1;
shader.uniforms.color2 = uniforms.color2;
shader.vertexShader = `
varying vec3 vPos;
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,`#include <begin_vertex>
vPos = transformed;
`
);
shader.fragmentShader = `
uniform vec3 bbMin;
uniform vec3 bbMax;
uniform vec3 color1;
uniform vec3 color2;
varying vec3 vPos;
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse,opacity );`,`
float f = clamp((vPos.z - bbMin.z) / (bbMax.z - bbMin.z),0.,1.);
vec3 col = mix(color1,color2,f);
vec4 diffuseColor = vec4( col,opacity );`
);
console.log(shader.vertexShader);
console.log(shader.fragmentShader);
}
});
let o = new THREE.Mesh(g,m);
scene.add(o);
renderer.setAnimationLoop(()=>{
renderer.render(scene,camera);
});
</script>
,
MeshStandardMaterial
着色器可编译为数百行代码,因此要在保持环境反射,照明,金属性,粗糙度以及所有这些行为的同时,精确指出更改颜色的位置非常困难。您可能想得太多。如果您只需要在MeshStandardMaterial
上渐变,为什么不只用渐变创建纹理,然后将其分配给Material.map
属性?如果添加白色的AmbientLight
,则应该可以毫无问题地看到渐变。
或者,如果希望渐变不受灯光影响,则可以将纹理分配给材质的.emissive
属性。
最后,如果要避免使用纹理,可以为每个顶点分配颜色,然后将Material.vertexColors
设置为true。