p5.js 中消失的 3D 对象面

3D Object Faces Disappearing in p5.js

我正在尝试使用以下代码制作一个每边都有图案的 3D 盒子,但是当从某些角度观看时,当透过正面的透明部分观看时,背面会消失。我也想知道是否可以在每张脸上都有不同的图案?非常感谢!

let r = 10
let a = 0
let c = 20
let angle = 0
let art

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  art = createGraphics(800, 800)

}

function draw() {
  background(0);
  
  let x = r + c * cos(a)
  let y = r + c * sin(a)

  art.fill(r, a, c)
  art.ellipse(x + 400, y + 400, 10, 10)

  c += 0.2
  a += 1.8

  push()
  texture(art)
  rotateX(angle)
  rotateY(angle)
  rotateZ(angle)
  box(400)

  angle += 0.0003
  pop()

  orbitControl();
}
html, body { margin: 0; overflow: hidden; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

发生这种情况是因为在 WebGL 中,一旦绘制了一个像素,无论该像素的透明度如何,如果另一个三角形绘制到同一像素,但在更深的深度,它就会被丢弃 (我认为来自原始像素的 alpha 信息可能不再可用)。为了使透明度在 WebGL 中正常工作,有必要按深度顺序绘制所有三角形(从相机最远的开始)。即使这样,两个三角形相交也会有问题。

在你的情况下,因为你有许多完全透明的像素和其他完全不透明的像素,所以还有另一种解决方案:自定义片段着色器,如果纹理 alpha 低于某个阈值则丢弃像素。

const vert = `
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;

attribute vec3 aPosition;
attribute vec2 aTexCoord;

varying vec2 vTexCoord;

void main() {
  vTexCoord = aTexCoord;

  vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
  gl_Position = uProjectionMatrix * viewModelPosition;
}`;

const frag = `
precision mediump float;

// ranges from 0..1
varying vec2 vTexCoord;

uniform sampler2D uSampler;

void main() {
  vec4 tex = texture2D(uSampler, vTexCoord);
  if (tex.a < 0.05) {
    discard;
  }

  gl_FragColor = tex;
}`;

let r = 10
let a = 0
let c = 20
let angle = 0
let art
let discardShader;

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  art = createGraphics(800, 800)
  discardShader = createShader(vert, frag)
  textureMode(NORMAL)
}

function draw() {
  background(0);

  let x = r + c * cos(a)
  let y = r + c * sin(a)

  art.fill(r, a, c)
  art.ellipse(x + 400, y + 400, 10, 10)

  c += 0.2
  a += 1.8

  push()
  noStroke()
  texture(art)
  shader(discardShader)
  rotateX(angle)
  rotateY(angle)
  rotateZ(angle)
  box(400)

  angle += 0.0003
  pop()

  orbitControl();
}
html,
body {
  margin: 0;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

注意#1:使用 p5.js v1.4.1 很重要,因为在此之前有一个错误阻止用户着色器使用纹理。

注意#2:如果你的纹理有部分不透明度,那么这将不起作用,相反你会想要以正确的顺序分别渲染盒子的每个平面(最远离相机的第一个)。