我的目标是将点数组传递给着色器,计算它们与片段的距离,并根据该计算为它们绘制一个带有渐变色的圆。

例如:

(来自working example I set up on shader toy)

不幸的是,我不清楚我应该如何计算和转换为着色器内部处理所传递的坐标。

我目前正在尝试的是通过均匀的方式将两个浮点数组传递给着色器,其中一个数组用于每个点的x位置,另一个用于y点的一个y位置。然后在着色器内部迭代遍历每个点,如下所示:

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

uniform float sourceX[100];
uniform float sourceY[100];
uniform vec2 resolution;

in vec4 gl_FragCoord;

varying vec4 vertColor;
varying vec2 center;
varying vec2 pos;

void main()
{
    float intensity = 0.0;

    for(int i=0; i<100; i++)
    {
        vec2 source = vec2(sourceX[i],sourceY[i]);
        vec2 position = ( gl_FragCoord.xy / resolution.xy );
        float d = distance(position, source);
        intensity += exp(-0.5*d*d);
    }

    intensity=3.0*pow(intensity,0.02);

    if (intensity<=1.0)
        gl_FragColor=vec4(0.0,intensity*0.5,0.0,1.0);
    else if (intensity<=2.0)
        gl_FragColor=vec4(intensity-1.0, 0.5+(intensity-1.0)*0.5,0.0,1.0);
    else
        gl_FragColor=vec4(1.0,3.0-intensity,0.0,1.0);
}

但这是行不通的-我相信这可能是因为我试图在不正确转换像素坐标的情况下对其进行处理。谁能向我解释如何进行这项工作?

更新:

当前结果是:

草图的代码为:
PShader pointShader;

float[] sourceX;
float[] sourceY;


void setup()
{

  size(1024, 1024, P3D);
  background(255);

  sourceX = new float[100];
  sourceY = new float[100];
  for (int i = 0; i<100; i++)
  {
    sourceX[i] = random(0, 1023);
    sourceY[i] = random(0, 1023);
  }


  pointShader = loadShader("pointfrag.glsl", "pointvert.glsl");
  shader(pointShader, POINTS);
  pointShader.set("sourceX", sourceX);
  pointShader.set("sourceY", sourceY);
  pointShader.set("resolution", float(width), float(height));
}


void draw()
{
  for (int i = 0; i<100; i++) {
    strokeWeight(60);
    point(sourceX[i], sourceY[i]);
  }
}

而顶点着色器是:
#define PROCESSING_POINT_SHADER

uniform mat4 projection;
uniform mat4 transform;


attribute vec4 vertex;
attribute vec4 color;
attribute vec2 offset;

varying vec4 vertColor;
varying vec2 center;
varying vec2 pos;

void main() {

  vec4 clip = transform * vertex;
  gl_Position = clip + projection * vec4(offset, 0, 0);

  vertColor = color;
  center = clip.xy;
  pos = offset;
}

最佳答案

更新:

根据评论,您似乎已经混淆了两种不同的方法:

  • 绘制一个全屏多边形,传递点,并使用着色器中的循环为每个片段计算一次最终值。
  • 绘制每个点的边界几何,在片段着色器中仅计算一个点的密度,并使用加法混合将所有点的密度求和。

  • 另一个问题是您的点以像素为单位,但是代码期望范围为0到1,因此d很大,点是黑色的。将此问题修复为@RetoKoradi describes应该可以解决黑点问题,但是我怀疑当许多距离很近时您会发现斜波削波问题。将点传递到着色器会限制可伸缩性,并且效率低下,除非这些点覆盖了整个视口(viewport)。

    如下所示,我认为坚持方法2更好。要为其重构代码,请删除循环,不要传递点数组,而应将center用作点坐标:
    //calc center in pixel coordinates
    vec2 centerPixels = (center * 0.5 + 0.5) * resolution.xy;
    
    //find the distance in pixels (avoiding aspect ratio issues)
    float dPixels = distance(gl_FragCoord.xy, centerPixels);
    
    //scale down to the 0 to 1 range
    float d = dPixels / resolution.y;
    
    //write out the intensity
    gl_FragColor = vec4(exp(-0.5*d*d));
    

    Draw this to a texture(来自注释:opengl-tutorial.org codethis question)与加法blending:
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    

    现在,该纹理将包含原始循环之后的intensity。在全屏传递过程中的另一个片段着色器中(绘制覆盖整个视口(viewport)的单个三角形),继续:
    uniform sampler2D intensityTex;
    ...
    float intensity = texture2D(intensityTex, gl_FragCoord.xy/resolution.xy).r;
    intensity = 3.0*pow(intensity, 0.02);
    ...
    

    假设您要绘制一个全屏多边形,那么片段显示器对于每个像素运行一次,则所显示的代码很好。潜在的问题是:
  • resolution设置不正确
  • 点坐标在屏幕上不在0到1的范围内。
  • 尽管较小,但d会被宽高比拉伸(stretch),因此您最好通过resolution.y将点缩放到像素坐标和潜水距离。

  • 这看起来与为2D metaballs创建密度字段非常相似。为了提高性能,最好限制每个点的密度函数,以使其永远不会持续,然后使用添加混合将光盘打成纹理。这样可以节省处理点不会影响的像素(就像延迟着色一样)。结果是密度字段,或者您的情况下是每像素intensity

    这些有点相关:
  • 2D OpenGL ES Metaballs on android(尚无答案)
  • calculate light volume radius from intensity
  • gl_PointSize Corresponding to World Space Size
  • 关于opengl - 统一点阵列和管理片段着色器坐标系,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31141429/

    10-15 23:59