英文:
three.js renders unexpected color values using points and custom shading and blending
问题
我有一个相对简单的设置,使用自定义着色器和自定义混合模式:
- 一个
Scene
和一个PerspectiveCamera
- 一个
IcosahedronBufferGeometry
- 一个自定义的
new THREE.ShaderMaterial
,带有自定义着色器 - 一些使用正二十面体和自定义着色器材质的
new THREE.Points
我的着色器材质具有自定义混合模式:
shaderMaterial.blending = THREE.CustomBlending;
shaderMaterial.blendSrc = THREE.OneFactor;
shaderMaterial.blendDst = THREE.OneFactor;
shaderMaterial.blendEquation = THREE.AddEquation;
而我的片段着色器目前只执行以下操作:
gl_FragColor = vec4(0.0, 0.1, 0.0, 0.0);
由于我渲染到完全黑色的画布,考虑混合模式,绘制点的期望颜色应该是:
SRC DST
r = 0 r = 0 r = 0
g = 0.1 * 1.0 + g = 0 * 1.0 = g = 0.1
b = 0 b = 0 b = 0
然而,我得到了完全不同的结果:
点比预期的 0.1
明亮得多。使用以下设置:
gl_FragColor = vec4(0.0, 0.2, 0.0, 0.0);
点变得完全饱和 (g = 255)
。
是什么原因导致了这个问题?我有一些想法,希望能指导我正确方向。
- 也许 three.js 在我的片段着色器中附加了一些代码,修改了
gl_FragColor
? - 也许我不知道 three.js 进行的一些额外渲染/后处理步骤?
- 也许我对混合模式的数学理解有误?
- 也许在 three.js 中存在某些实现错误?
- 也许这是浏览器/画布/颜色空间的问题?(但是,我无法想象任何变换会导致绿色值从 0.2 变成完全饱和(255)在任何颜色空间变换中!)
非常感谢任何帮助!
谢谢!
英文:
I have a relatively simple setup with custom shader and a custom blend mode:
- A
Scene
and aPerspectiveCamera
- An
IcosahedronBufferGeometry
- A custom
new THREE.ShaderMaterial
with custom shaders - Some
new THREE.Points
with the icosahedron and that custom shader material
My shader material has a custom blend mode:
shaderMaterial.blending = THREE.CustomBlending;
shaderMaterial.blendSrc = THREE.OneFactor;
shaderMaterial.blendDst = THREE.OneFactor;
shaderMaterial.blendEquation = THREE.AddEquation;
and my fragment shader currently only does this:
gl_FragColor = vec4(0.0, 0.1, 0.0, 0.0);
Since I'm rendering into a fully black canvas, the expected color of drawn points should be, considering the blending mode:
SRC DST
r = 0 r = 0 r = 0
g = 0.1 * 1.0 + g = 0 * 1.0 = g = 0.1
b = 0 b = 0 b = 0
However, I get a totally different result:
The points are rendered much more brightly than the expected 0.1
. Using
gl_FragColor = vec4(0.0, 0.2, 0.0, 0.0);
the points become fully saturated (g = 255)
.
What causes this? I have some ideas and hopefully can push me in the right direction.
- Perhaps three.js appends some code to my fragment shader, modifying the
gl_FragColor
? - Maybe I'm not aware of some extra rendering / post processing step that three.js does?
- Maybe I've misunderstood the math regarding blending modes?
- Maybe there is some implementation error in three.js?
- Perhaps it's a browser / canvas / color space thing? (However, I cannot imagine any transformation that causes a green value of 0.2 to become fully saturated (255) in any color space transformation!)
Any help would be greatly appreciated.
Thanks!
答案1
得分: 1
问题在于每个点被绘制了5次,因为IcosahedronBufferGeometry
是为绘制三角形而设计的。每个顶点被5个三角形共享。我们可以通过增加drawArrays
的测试来验证这一点。
运行下面的代码,我们可以看到它要求绘制60个点,尽管从视觉上只有12个(下面的代码不绘制任何可见的内容,它只是用来检查绘制调用)。
WebGLRenderingContext.prototype.drawArrays = function(origFn) {
return function(...args) {
console.log('drawArrays', ...args);
origFn.call(this, ...args);
};
}(WebGLRenderingContext.prototype.drawArrays);
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const camera = new THREE.Camera();
const scene = new THREE.Scene();
const geo = new THREE.IcosahedronBufferGeometry(1);
const material = new THREE.MeshBasicMaterial();
scene.add(new THREE.Points(geo, material));
renderer.render(scene, camera);
}
main();
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
英文:
The issue is every point is being drawn 5 times because IcosahedronBufferGeometry
is designed for drawing triangles. Each vertex is shared by 5 triangles. We can test this by augmenting drawArrays
Running the code below we see it's asking to draw 60 points even though visually there would only be 12 (the code below does not draw anything visible, it's only designed to check the draw call).
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
WebGLRenderingContext.prototype.drawArrays = function(origFn) {
return function(...args) {
console.log('drawArrays', ...args);
origFn.call(this, ...args);
};
}(WebGLRenderingContext.prototype.drawArrays);
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const camera = new THREE.Camera();
const scene = new THREE.Scene();
const geo = new THREE.IcosahedronBufferGeometry(1);
const material = new THREE.MeshBasicMaterial();
scene.add(new THREE.Points(geo, material));
renderer.render(scene, camera);
}
main();
<!-- language: lang-html -->
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论