在顶点着色器中计算颜色值?
Calculating color values in vertex shader?
所以我目前正在使用一些噪声函数来创建一些程序地形。根据我的理解,我们可以使用一个噪声函数,它接受一个二维向量和 returns 一个浮点数。然后我们可以将这个浮点数解释为 space 中那个点的 "height"。我们还可以将此浮点数解释为相应片段的颜色。结果,我们最终得到顶部为白色、底部为黑色的山脉。
现在,我基本上在顶点和片段着色器中进行相同的计算以获得相同的值:
顶点着色器:
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
}
但是,当我尝试这样做时,传递的值似乎并不相同。高度图完全消失。我猜这与片段着色器为每个片段而不是每个顶点计算颜色这一事实有关?究竟是怎么回事?我可以通过哪些方式优化此代码?感觉没必要重复这么多代码。
而 Vertex Shader is executed for each vertex of the vertices of the mesh buffer, the Fragment Shader is executed at least once for each fragment which is drawn. Output variables from the Vertex Shader are passed to the next stage of the pipeline.
If the next stage is the Fragment Shader (which is the case in WebGL) the output varibales of the Vertex Shader are interpolated according to there Barycentric coordinates on the rendered primitive, and are passed to the input varibales of the Fragment Shader.
请注意,gl_Position
每个顶点只计算一次,而 gl_FragColor
每个片段计算一次。这导致 Vertex Shader defines the geometry and the Fragment 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);
}
片段着色器
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 );
}
注意,我添加了一个函数,可以像彩虹一样为高度图着色。
所以我目前正在使用一些噪声函数来创建一些程序地形。根据我的理解,我们可以使用一个噪声函数,它接受一个二维向量和 returns 一个浮点数。然后我们可以将这个浮点数解释为 space 中那个点的 "height"。我们还可以将此浮点数解释为相应片段的颜色。结果,我们最终得到顶部为白色、底部为黑色的山脉。
现在,我基本上在顶点和片段着色器中进行相同的计算以获得相同的值:
顶点着色器:
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
}
但是,当我尝试这样做时,传递的值似乎并不相同。高度图完全消失。我猜这与片段着色器为每个片段而不是每个顶点计算颜色这一事实有关?究竟是怎么回事?我可以通过哪些方式优化此代码?感觉没必要重复这么多代码。
而 Vertex Shader is executed for each vertex of the vertices of the mesh buffer, the Fragment Shader is executed at least once for each fragment which is drawn. Output variables from the Vertex Shader are passed to the next stage of the pipeline.
If the next stage is the Fragment Shader (which is the case in WebGL) the output varibales of the Vertex Shader are interpolated according to there Barycentric coordinates on the rendered primitive, and are passed to the input varibales of the Fragment Shader.
请注意,gl_Position
每个顶点只计算一次,而 gl_FragColor
每个片段计算一次。这导致 Vertex Shader defines the geometry and the Fragment 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);
}
片段着色器
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 );
}
注意,我添加了一个函数,可以像彩虹一样为高度图着色。