微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从片段着色器恢复像素数据会导致意外行为

如何解决从片段着色器恢复像素数据会导致意外行为

我目前正在尝试使用片段着色器来传输一些粒子位置数据(并在将来修改它)。我使用 sampler2D 纹理将数据发送到着色器没有问题,但是当我尝试恢复数据时,我的 20 个粒子突然出现了错误的位置。我已经打印了尽可能多的输出,并尽可能减少了代码,但仍然无法理解我错在哪里。

在 p5js 网站 here

上提供了可重现的最小化版本

这是我的 Sketch.js :

let theShader;
let shaderTexture;

let NUM_PARTICLES = 20;
let particleTexture;
let particles = [];

function preload(){
    theShader = loadShader('basic.vert','basic.frag');
}

function setup() {
  createCanvas(400,400,WEBGL);
  
  // Initiate Particles
  for(let i = 0; i < NUM_PARTICLES;i++){
    particles.push(createVector(i*20,i*20,0));
  }
  
  // Initialize Shader
  shaderTexture = createGraphics(NUM_PARTICLES,1,WEBGL);
  shaderTexture.nostroke();
  
  // Create Particle Texture
  particleTexture = createImage(NUM_PARTICLES,1);
  
  // Fill Particle Texture
  particleTexture.loadPixels();
  for (let i = 0; i < NUM_PARTICLES; i++) {   
    particleTexture.pixels[i*4+0] = map(particles[i].x,width,255); // R
    particleTexture.pixels[i*4+1] = map(particles[i].y,height,255); // G
    particleTexture.pixels[i*4+2] = 0; // B
    particleTexture.pixels[i*4+3] = 255; // A
  }
  particleTexture.updatePixels();
}

function draw() {  
  translate(-width/2,-height/2);
  background(255);
  
  // display Particles Before Modification
  for(let i = 0; i < NUM_PARTICLES;i++){
    circle(particles[i].x,particles[i].y,10); // draw circle at particle location
  }

  // Apply Texture
  shaderTexture.shader(theShader); // set shader
  theShader.setUniform('text',particleTexture); // send particleTexture to shader
  shaderTexture.rect(0,NUM_PARTICLES,1); // set rect to recieve shader out
  
  // Print Shader Output
  for(let i = 0; i < NUM_PARTICLES;i++){
    let newPos = shaderTexture.get(i,0);
    print(newPos);
  }
  
  // Update and display Particles
  for(let i = 0; i < NUM_PARTICLES;i++){
    let newPos = shaderTexture.get(i,0);
    particles[i].x = map(newPos[0],255,width);
    particles[i].y = map(newPos[1],height);
    fill(255,0);
    circle(particles[i].x,10);
  }

  noLoop();
}

这是我的片段着色器,它不应修改任何内容

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D text;

void main() {
  vec2 particle = texture2D(text,vec2(gl_FragCoord.x,gl_FragCoord.y)).xy;

  gl_FragColor = vec4(particle.x,particle.y,0.0,1.0); // R,G,B,A
}

也是我的认顶点着色器:

#ifdef GL_ES
precision highp float;
#endif

attribute vec3 aPosition;

void main() {
  vec4 positionVec4 = vec4(aPosition,1.0);

  positionVec4.xy = positionVec4.xy * 2.0 - 1.0; 

  gl_Position = positionVec4;
}

解决方法

使用 texture2D 查找纹理时,纹理坐标必须在 [0.0,1.0] 范围内指定。 (0,0) 是左下角, (1,1) 是右上角。但是 gl_FragCoord.xy 包含窗口坐标,左上角 (0.5,0.5) 和右上角 (width-0.5,height-0.5)。
因此,您需要将 gl_FragCoord 除以视口的大小。由于您在大小为 NUM_PARTICLESx1(宽度 == NUM​​_PARTICLES,高度 = 1)的矩形上绘制,您必须将 gl_FragCoord.x 除以 NUM_PARTICLES

vec2 particle = texture2D(text,vec2(gl_FragCoord.x,gl_FragCoord.y)).xy;

vec2 particle = texture2D(text,vec2(gl_FragCoord.x/20.0,0.0)).xy;

或者,您可以向片段着色器添加 size 统一并将 gl_FragCoord.xy 除以 size

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D text;
uniform vec2 size;

void main() {
  
  vec2 particle = texture2D(text,gl_FragCoord.xy / size).xy;

  gl_FragColor = vec4(particle.x,particle.y,0.0,1.0); // R,G,B,A
}

通过size设置[NUM_PARTICLES,1]统一的值:

// Apply Texture
shaderTexture.shader(theShader); // set shader
theShader.setUniform('text',particleTexture); // send particleTexture to shader
theShader.setUniform('size',[NUM_PARTICLES,1]);
shaderTexture.rect(0,NUM_PARTICLES,1); // set rect to recieve shader out


如果您使用 OpenGL ES Shading Language 3.00,您可以选择使用 texelFetch 查找具有整数像素坐标的纹理或使用 textureSize 获取纹理的大小。
不幸的是,p5.js (createCanvas()) 似乎没有提供 WebGL 2.0 上下文,所以这不是一个选项。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。