three.js使用点和自定义着色与混合渲染出意外的颜色值。

huangapple go评论51阅读模式
英文:

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

然而,我得到了完全不同的结果:

three.js使用点和自定义着色与混合渲染出意外的颜色值。

点比预期的 0.1 明亮得多。使用以下设置:

gl_FragColor = vec4(0.0, 0.2, 0.0, 0.0);

点变得完全饱和 (g = 255)

是什么原因导致了这个问题?我有一些想法,希望能指导我正确方向。

  1. 也许 three.js 在我的片段着色器中附加了一些代码,修改了 gl_FragColor
  2. 也许我不知道 three.js 进行的一些额外渲染/后处理步骤?
  3. 也许我对混合模式的数学理解有误?
  4. 也许在 three.js 中存在某些实现错误?
  5. 也许这是浏览器/画布/颜色空间的问题?(但是,我无法想象任何变换会导致绿色值从 0.2 变成完全饱和(255)在任何颜色空间变换中!)

非常感谢任何帮助!
谢谢!

英文:

I have a relatively simple setup with custom shader and a custom blend mode:

  • A Scene and a PerspectiveCamera
  • 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:

three.js使用点和自定义着色与混合渲染出意外的颜色值。

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.

  1. Perhaps three.js appends some code to my fragment shader, modifying the gl_FragColor?
  2. Maybe I'm not aware of some extra rendering / post processing step that three.js does?
  3. Maybe I've misunderstood the math regarding blending modes?
  4. Maybe there is some implementation error in three.js?
  5. 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(&#39;drawArrays&#39;, ...args);
    origFn.call(this, ...args);
  };
}(WebGLRenderingContext.prototype.drawArrays);

function main() {
  const canvas = document.querySelector(&#39;#c&#39;);
  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 -->

&lt;canvas id=&quot;c&quot;&gt;&lt;/canvas&gt;
&lt;script src=&quot;https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2020年1月3日 20:47:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/59578919.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定