在顶点着色器中计算颜色值?

时间:2017-08-18 06:40:22

标签: webgl

所以我目前正在使用一些噪声函数来创建一些程序性地形。根据我的理解,我们可以使用一个带有2D矢量并返回浮点数的噪声函数。然后我们可以将这个浮点数解释为该点在空间中的“高度”。我们还可以将此float解释为相应片段的颜色。结果,我们最终得到的山顶是白色的,底部是黑色的。

现在,我基本上在顶点和片段着色器中进行相同的计算以获得相同的值:

顶点着色器:

uniform sampler2D texture;
uniform float time;
uniform float speed;

varying vec3 pos;
varying vec2 vUv;

float random (in vec2 st) { 
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))* 
        43758.5453123);
}

// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(a, b, u.x) + 
            (c - a)* u.y * (1.0 - u.x) + 
            (d - b) * u.x * u.y;
}

#define OCTAVES 8
float fbm ( vec2 st) {
    // Initial values
    float value = 0.;
    float amplitud = .5;
    float frequency = 0.;
    //
    // Loop of octaves
    for (int i = 0; i < OCTAVES; i++) {
        value += amplitud * noise(st);
        st *= 2.1;
        amplitud *= .6;
    }
    return value;
}

float pattern( in vec2 p )
  {
      vec2 q = vec2( fbm( p + vec2(0.0,0.0) ),
                     fbm( p + vec2(5.2,1.3) ) );

      vec2 r = vec2( fbm( p + 4.0*q + vec2(1.7,9.2) ),
                     fbm( p + 4.0*q + vec2(8.3,2.8) ) );

      return fbm( p + 4.0*r );
  }

void main(){
    vUv = uv + time;
    pos = position;

    float n = pattern(pos.xy);

    gl_Position = projectionMatrix * modelViewMatrix * vec4(position + 
    normal*n*.035, 1.);
}

片段着色器:

uniform sampler2D texture;
uniform float time;

varying vec2 vUv;
varying vec3 pos;

float random (in vec2 st) { 
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))* 
        43758.5453123);
}

// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(a, b, u.x) + 
            (c - a)* u.y * (1.0 - u.x) + 
            (d - b) * u.x * u.y;
}

#define OCTAVES 8
float fbm ( vec2 st) {
    // Initial values
    float value = 0.;
    float amplitud = .5;
    float frequency = 0.;
    //
    // Loop of octaves
    for (int i = 0; i < OCTAVES; i++) {
        value += amplitud * noise(st);
        st *= 2.1;
        amplitud *= .6;
    }
    return value;
}

float pattern( in vec2 p )
  {
      vec2 q = vec2( fbm( p + vec2(0.0,0.0) ),
                     fbm( p + vec2(5.2,1.3) ) );

      vec2 r = vec2( fbm( p + 4.0*q + vec2(1.7,9.2) ),
                     fbm( p + 4.0*q + vec2(8.3,2.8) ) );

      return fbm( p + 4.0*r );
  }

void main(){
    vec2 q; 
    vec2 r;
    vec2 j = vec2(0., 1.);
    float p = pattern(pos.xy);

    vec4 color = texture2D(texture, vUv + p/5.);

    // gl_FragColor = vec4(color.rgb, p);
    gl_FragColor = vec4(p);
}

顶点和片段着色器都调用pattern(pos.xy),它产生相同的值。所以我想通过为每个顶点计算一次这个值并使用变化的浮点数将其传递到片段着色器中,我可以使代码更有效:

//vertex shader
varying float noise;

void main(){
    noise = pattern(position.xy);
    //rest of code
}

//fragment shader
varying float noise;
void main(){
    //do something with noise value
}

但是,当我尝试这个时,传递的值似乎不一样。高度图完全消失。我猜这与片段着色器为每个片段而不是每个顶点计算颜色的事实有关?到底发生了什么?有什么方法可以优化这段代码?我觉得没有必要重复这么多代码。

1 个答案:

答案 0 :(得分:1)

虽然对网格缓冲区的顶点的每个顶点执行Vertex Shader,但对于绘制的每个片段,Fragment Shader至少执行一次。来自Vertex Shader的输出变量将传递到管道的下一阶段。
如果下一阶段是Fragment Shader(WebGL中的情况),则{{3}的输出变量根据渲染图元上的Vertex Shader进行插值,并传递给Barycentric coordinates的输入变量。

注意,虽然每个顶点只计算gl_Position一次,但每个片段计算一次gl_FragColor。这导致Fragment Shader定义几何,Vertex Shader定义片段的颜色。

出于优化的原因,您可以计算每个顶点一次高度贴图的高度,并让图形管道完成工作以插入顶点之间碎片着色的高度。

你的代码看起来应该是这样的:

顶点着色器

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;

uniform float time;
uniform float speed;

varying vec3  pos;
varying vec2  vUv;
varying float mapH;

float pattern( in vec2 p );

void main()
{
    vUv         = uv + time;
    pos         = position;
    mapH        = pattern(pos.xy);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position + normal*mapH*.035, 1.0);
}

Frgament Shader

varying vec2  vUv;
varying vec3  pos;
varying float mapH;

vec3 HeightToRGB(in float H)
{
    float B = abs(H * 5.0 - 3.0) - 1.0;
    float G = 2.0 - abs(H * 5.0 - 2.0);
    float R = 2.0 - abs(H * 5.0 - 4.0);
    return clamp( vec3(R,G,B), 0.0, 1.0 );
}

void main()
{
    vec3 color = HeightToRGB( clamp(mapH, 0.0, 1.0) );
    gl_FragColor = vec4( color, 1.0 );
}

注意,我添加了一个函数,它将高度图的颜色设置为类似彩虹。