我正在尝试在 Threejs 中集成一个 webgl 高斯泼溅渲染器,为此我正在创建一个自定义网格类(根据文档这是可能的)。
创建类时,我获取数据,上传数据,然后创建实例化几何体:
const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = count;
geometry.setAttribute("splatPosition", new THREE.BufferAttribute(new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]), 2));
const indexBuffer = new Uint32Array(count); // Buffer is updated on a later stage
const indexAttrib = new THREE.InstancedBufferAttribute(indexBuffer, 1, false);
indexAttrib.setUsage(THREE.DynamicDrawUsage);
indexAttrib.needsUpdate = true;
geometry.setAttribute("splatIndex", indexAttrib);
this.geometry = geometry;
构造函数的开始:
super(new THREE.SphereGeometry(0.5, 32, 32), new THREE.MeshStandardMaterial({color: 0x00ff00}));
// this.renderOrder = -100; // Changes nothing
this.frustumCulled = false;
this.visible = true;
this.castShadow = false;
this.loaded = false;
材质创作:
const material = new THREE.RawShaderMaterial({
uniforms: {
focal: {type: 'v2', value: new THREE.Vector2()},
viewport: {type: 'v2', value: new THREE.Vector2()},
time: {type: 'f', value: 0},
opacity: {type: 'f', value: 1.0},
texture: {type: 't', value: null}
},
glslVersion: THREE.GLSL3,
vertexShader: vertexShaderSource,
fragmentShader: fragmentShaderSource,
side: THREE.DoubleSide,
blending: THREE.CustomBlending,
blendDst: THREE.OneMinusDstAlphaFactor,
blendDstAlpha: THREE.OneMinusDstAlphaFactor,
blendSrc: THREE.OneFactor,
blendSrcAlpha: THREE.OneFactor,
blendEquation: THREE.AddEquation,
depthTest: false,
depthWrite: false,
forceSinglePass: true,
transparent: false
});
我的材料已正确编译,具有正确的属性并且制服已设置。我在 onBeforeRender 中更新了制服,这是正确调用的。但是,随后不会调用 opengl 来绘制模型。
请注意,如果我用球体和默认材质替换实例化几何体和着色器,则一切正常。我的节点也已正确添加到场景中。
用球体替换了自定义实例地理,本来不应该起作用,但它确实起作用了。尝试覆盖 InstancedMesh 而不是 Mesh,单独尝试 BufferGeometry。什么也没做。
更新
我只是忘记在我的几何体中设置绘制范围。因为我有 2 个三角形要渲染,所以我只需正确放置我的顶点,然后就可以了
geometry.drawRange.count = 6;
使用自定义网格类将自定义 WebGL 高斯泼溅渲染器集成到 Three.js 中涉及正确创建和设置自定义着色器材质和实例化几何体。根据您的描述,听起来自定义实例几何体未渲染,即使材质和几何体设置似乎正确。
// Vertex Shader
const vertexShaderSource = `
#version 300 es
in vec2 splatPosition;
in float splatIndex;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(splatPosition, 0.0, 1.0);
gl_Position = projectionMatrix * mvPosition;
}
`;
// Fragment Shader
const fragmentShaderSource = `
#version 300 es
precision highp float;
uniform float time;
out vec4 outColor;
void main() {
outColor = vec4(abs(sin(time)), 0.0, 0.0, 1.0);
}
`;
class CustomMesh extends THREE.Mesh {
constructor(count) {
const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = count;
geometry.setAttribute("splatPosition", new THREE.BufferAttribute(new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]), 2));
const indexBuffer = new Uint32Array(count);
const indexAttrib = new THREE.InstancedBufferAttribute(indexBuffer, 1, false);
indexAttrib.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute("splatIndex", indexAttrib);
const material = new THREE.RawShaderMaterial({
uniforms: {
time: { type: 'f', value: 0 }
},
glslVersion: THREE.GLSL3,
vertexShader: vertexShaderSource,
fragmentShader: fragmentShaderSource,
side: THREE.DoubleSide,
blending: THREE.CustomBlending,
blendDst: THREE.OneMinusDstAlphaFactor,
blendDstAlpha: THREE.OneMinusDstAlphaFactor,
blendSrc: THREE.OneFactor,
blendSrcAlpha: THREE.OneFactor,
blendEquation: THREE.AddEquation,
depthTest: false,
depthWrite: false,
forceSinglePass: true,
transparent: false
});
super(geometry, material);
this.frustumCulled = false;
this.visible = true;
this.castShadow = false;
this.loaded = false;
}
}
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const customMesh = new CustomMesh(10);
scene.add(customMesh);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
// Update time uniform
customMesh.material.uniforms.time.value = clock.getElapsedTime();
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});