shader基础-颜色

337 阅读4分钟

1. 变量的访问方式

vec4 vector; 
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p; 
vector[3] = vector.a = vector.w = vector.q;

2. 变量的投射和混合

这些指向向量内部变量的不同方式仅仅是设计用来帮助你写出干净代码的术语。着色语言所包含的灵活性为你互换地思考颜色和坐标位置。 GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合(变量)值。这种能力被(形象地)称为:鸡尾酒

vec3 yellow, magenta, green; 

// Making Yellow 
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels 
yellow[2] = 0.0; // Assigning 0. to blue channel 

// Making Magenta 
magenta = yellow.rbg; // Assign the channels with green and blue swapped 

// Making Green green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels

3. mix()颜色混合

GLSL内置的mix函数会在两个数据之间插值。 在GLSL中,mix()函数用于进行线性插值。它接受三个参数:两个值和一个混合因子。混合因子用于控制应从第一个值向第二个值插入多少。

这是mix()函数的基本定义:

genType mix(genType x, genType y, float a);
genDType mix(genDType x, genDType y, double a);
genType mix(genType x, genType y, genType a);
genDType mix(genDType x, genDType y, genDType a);

在这里,“genType”是指任何类型的向量(float、vec2、vec3、vec4)或者单独的浮点数(float)。"genDType" 是对应于 double 的向量类型或单独的 double 类型。

函数的行为是:当a等于0.0时,返回x,当a等于1.0时,返回y。对于介于0.0和1.0之间的a值,返回的是x和y的线性插值。如果 a 的值超过了 [0, 1] 的范围,那么结果会超出 x 和 y 的范围。

例如:

float a = 0.75;
vec3 color1 = vec3(1.0, 0.0, 0.0);  // 红色
vec3 color2 = vec3(0.0, 1.0, 0.0);  // 绿色
vec3 result = mix(color1, color2, a);

在这个例子中,result会是一个从红色(color1)到绿色(color2)之间的色彩,更偏向于绿色,因为a的值为0.75。

这段代码创建了一个动态的渐变效果,颜色会在深蓝色和黄色之间变化

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);

void main() {
    vec3 color = vec3(0.0);

    float pct = abs(sin(u_time));

    // Mix uses pct (a value from 0-1) to
    // mix the two colors
    color = mix(colorA, colorB, pct);

    gl_FragColor = vec4(color,1.0);
}

  1. float pct = abs(sin(u_time));:计算一个基于时间的百分比值,值的范围在0到1之间。由于使用了sin函数,这个值会随着时间的推移在0和1之间往复变化,产生一个动态的效果。
  2. color = mix(colorA, colorB, pct);:这行代码使用GLSL内置的mix函数,根据pct值在colorA和colorB之间插值。当pct为0时,结果是colorA;当pct为1时,结果是colorB。因此,由于pct的值在0和1之间动态变化,输出的颜色也会在colorA和colorB之间动态变化。

HSB的使用

除了rgb hsb也经常使用,配合极坐标如经常使用的调色板。

#ifdef GL_ES
precision mediump float;
#endif

#define TWO_PI 6.28318530718

uniform vec2 u_resolution;
uniform float u_time;

//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                             6.0)-3.0)-1.0,
                     0.0,
                     1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix( vec3(1.0), rgb, c.y);
}

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution;
    vec3 color = vec3(0.0);

    // Use polar coordinates instead of cartesian
    vec2 toCenter = vec2(0.5)-st;
    float angle = atan(toCenter.y,toCenter.x);
    float radius = length(toCenter)*2.0;

    // Map the angle (-PI to PI) to the Hue (from 0 to 1)
    // and the Saturation to the radius
    color = hsb2rgb(vec3((angle/TWO_PI)+0.5,radius,1.0));

    gl_FragColor = vec4(color,1.0);
}

这段 GLSL 代码是一个片段着色器,它的主要功能是使用 HSV(色调,饱和度,亮度)色彩空间,实现了一个在屏幕上呈放射状的彩虹色渐变效果。这是代码的具体解析:

  1. 定义精度和常数:片段着色器开始时,首先定义了所使用的浮点数的精度为中等精度(mediump float),并且定义了一个表示2π的宏(TWO_PI)。

  2. 初始化统一变量:初始化了一些预设的统一变量(uniforms):u_resolution(渲染区域的分辨率)和u_time(当前的时间)。

  3. 定义 HSV 到 RGB 的转换函数:在这部分,定义了一个将 HSV 色彩空间转换为 RGB 色彩空间的函数 hsb2rgb

  4. 主函数main 函数是着色器的入口点,以下是主函数的各个部分:

    • 计算标准化屏幕坐标:首先,使用当前的片元坐标(gl_FragCoord.xy)除以屏幕分辨率(u_resolution)得到一个范围在0和1之间的标准化屏幕坐标 st
    • 计算极坐标:然后,计算出片元到屏幕中心点的向量 toCenter,并使用 atanlength 函数计算出这个向量的角度 angle 和半径 radius
    • 计算 HSV 色彩:将角度 angle 映射到色调(Hue),半径 radius 映射到饱和度(Saturation),并将亮度(Value)设定为1,得到一个 HSV 色彩。
    • 转换为 RGB 色彩并设置片元颜色:使用之前定义的 hsb2rgb 函数将 HSV 色彩转换为 RGB 色彩,并赋值给 gl_FragColor。这个特殊的变量用于存储该片元的颜色,这个颜色最后会被用于渲染屏幕上的像素。