2.8 颜色和混合 (Color and Blending)


文档摘要

2.8 颜色和混合 (Color and Blending) WebGL 核心概念:2.8 颜色和混合 (Color and Blending) 详解 在 WebGL 图形渲染管线中,颜色和混合是至关重要的环节。它们决定了最终呈现在屏幕上的像素色彩,以及不同图形元素之间如何相互作用。理解颜色和混合的概念,能够帮助我们创建更丰富、更具表现力的视觉效果,例如透明度、半透明效果、粒子效果、光照效果等等。 2.8.1 WebGL 中的颜色表示 在 WebGL 中,颜色通常使用 RGBA (Red, Green, Blue, Alpha) 模式表示。每个颜色分量(红、绿、蓝、透明度)都使用 0.0 到 1.0 范围内的浮点数表示,这被称为 归一化颜色值。

2.8 颜色和混合 (Color and Blending)

WebGL 核心概念:2.8 颜色和混合 (Color and Blending) 详解

在 WebGL 图形渲染管线中,颜色和混合是至关重要的环节。它们决定了最终呈现在屏幕上的像素色彩,以及不同图形元素之间如何相互作用。理解颜色和混合的概念,能够帮助我们创建更丰富、更具表现力的视觉效果,例如透明度、半透明效果、粒子效果、光照效果等等。

2.8.1 WebGL 中的颜色表示

在 WebGL 中,颜色通常使用 RGBA (Red, Green, Blue, Alpha) 模式表示。每个颜色分量(红、绿、蓝、透明度)都使用 0.0 到 1.0 范围内的浮点数表示,这被称为 归一化颜色值

  • 红色 (Red, R): 表示颜色中红色的强度,0.0 表示没有红色,1.0 表示最大红色强度。

  • 绿色 (Green, G): 表示颜色中绿色的强度,0.0 表示没有绿色,1.0 表示最大绿色强度。

  • 蓝色 (Blue, B): 表示颜色中蓝色的强度,0.0 表示没有蓝色,1.0 表示最大蓝色强度。

  • 透明度 (Alpha, A): 表示颜色的不透明度,0.0 表示完全透明,1.0 表示完全不透明。介于 0.0 和 1.0 之间的值表示半透明。

例如:

  • vec4(1.0, 0.0, 0.0, 1.0) 表示纯红色,完全不透明。

  • vec4(0.0, 1.0, 0.0, 0.5) 表示半透明的绿色。

  • vec4(0.5, 0.5, 0.5, 1.0) 表示灰色,完全不透明。

  • vec4(1.0, 1.0, 1.0, 0.0) 表示完全透明的白色(实际上不可见)。

在 WebGL 程序中,颜色值通常以 vec4 类型(四维向量)存储和传递,尤其是在着色器程序中。

数据类型和精度:

WebGL 着色器语言 (GLSL ES) 中,颜色分量通常使用 float 类型,即单精度浮点数。在顶点着色器和片元着色器之间传递颜色数据时,可以使用 varying vec4 类型的变量。

颜色来源:

WebGL 中的颜色可以来自多个来源:

  • 顶点属性 (Vertex Attributes): 可以为每个顶点指定颜色属性,例如使用 gl.vertexAttribPointer() 设置顶点颜色数据。这通常用于实现顶点颜色渐变等效果。

  • Uniform 变量 (Uniform Variables): 可以使用 gl.uniform4f()gl.uniform4fv() 设置 uniform 变量,在着色器中作为常量颜色值使用。这适用于整个绘制调用颜色一致的情况。

  • 纹理 (Textures): 纹理可以包含像素颜色信息,通过纹理采样可以在片元着色器中获取纹理颜色。

  • 着色器计算 (Shader Calculation): 颜色也可以在顶点着色器或片元着色器中通过数学计算动态生成。例如,根据光照模型、材质属性等计算颜色。

2.8.2 WebGL 混合 (Blending) 原理

什么是混合?

WebGL 混合是一种将 源颜色 (Source Color)目标颜色 (Destination Color) 组合在一起的技术。

  • 源颜色 (Source Color): 指即将被绘制的片元颜色,通常由片元着色器输出。

  • 目标颜色 (Destination Color): 指帧缓冲区 (Framebuffer) 中已经存在的颜色,即当前像素位置的颜色。

混合的目标是将源颜色和目标颜色按照一定的规则进行混合,生成新的颜色,并将这个新颜色写入帧缓冲区,最终显示在屏幕上。

为什么需要混合?

混合的主要用途包括:

  • 实现透明和半透明效果: 通过混合,可以使物体看起来是透明或半透明的,允许背景颜色透过前景物体显示出来。例如,模拟玻璃、水、烟雾等效果。

  • 实现特效: 混合可以用于创建各种视觉特效,例如粒子效果、光晕效果、发光效果、火焰效果等。

  • 抗锯齿 (Anti-aliasing): 在某些抗锯齿技术中,混合也扮演着重要的角色。

混合过程详解:

WebGL 的混合过程可以概括为以下步骤:

  1. 获取源颜色 (Source Color): 片元着色器计算并输出当前片元的颜色,作为源颜色。

  2. 获取目标颜色 (Destination Color): 从帧缓冲区读取当前片元位置已有的颜色,作为目标颜色。

  3. 计算混合因子 (Blend Factors): 根据设置的混合函数,计算出源混合因子 (Source Factor, S) 和目标混合因子 (Destination Factor, D)。

  4. 执行混合操作 (Blend Operation): 根据设置的混合方程,将源颜色、目标颜色以及混合因子进行数学运算,得到混合后的颜色。

  5. 写入帧缓冲区 (Framebuffer): 将混合后的颜色写入帧缓冲区,替换原来的目标颜色。

可以用 Mermaid 图表来表示混合过程:

关键配置:混合函数和混合方程

WebGL 提供了灵活的配置选项来控制混合过程,主要通过以下两个函数进行设置:

  • gl.blendFunc(sfactor, dfactor) (混合函数): 设置源混合因子 (sfactor) 和目标混合因子 (dfactor)。

  • gl.blendEquation(mode) (混合方程): 设置混合操作 (mode),即如何将源颜色、目标颜色以及混合因子组合起来。

2.8.2.1 混合函数 (gl.blendFunc)

gl.blendFunc(sfactor, dfactor) 函数接受两个参数:

  • sfactor (源混合因子): 指定如何计算源颜色的混合因子。

  • dfactor (目标混合因子): 指定如何计算目标颜色的混合因子。

WebGL 提供了多种预定义的混合因子常量,常用的包括:

常量名称 源因子 (S) 的计算方式 目标因子 (D) 的计算方式 描述
gl.ZERO (0, 0, 0, 0) (0, 0, 0, 0) 零,颜色分量乘以 0
gl.ONE (1, 1, 1, 1) (1, 1, 1, 1) 一,颜色分量乘以 1
gl.SRC_COLOR (Rs, Gs, Bs, As) (源颜色) (Rd, Gd, Bd, Ad) (目标颜色) 源颜色
gl.ONE_MINUS_SRC_COLOR (1-Rs, 1-Gs, 1-Bs, 1-As) (1-Rd, 1-Gd, 1-Bd, 1-Ad) 一减去源颜色
gl.DST_COLOR (Rd, Gd, Bd, Ad) (目标颜色) (Rd, Gd, Bd, Ad) (目标颜色) 目标颜色
gl.ONE_MINUS_DST_COLOR (1-Rd, 1-Gd, 1-Bd, 1-Ad) (1-Rd, 1-Gd, 1-Bd, 1-Ad) 一减去目标颜色
gl.SRC_ALPHA (As, As, As, As) (源颜色的 Alpha 值) (Ad, Ad, Ad, Ad) (目标颜色的 Alpha 值) 源颜色的 Alpha 值
gl.ONE_MINUS_SRC_ALPHA (1-As, 1-As, 1-As, 1-As) (1-Ad, 1-Ad, 1-Ad, 1-Ad) 一减去源颜色的 Alpha 值
gl.DST_ALPHA (Ad, Ad, Ad, Ad) (目标颜色的 Alpha 值) (Ad, Ad, Ad, Ad) (目标颜色的 Alpha 值) 目标颜色的 Alpha 值
gl.ONE_MINUS_DST_ALPHA (1-Ad, 1-Ad, 1-Ad, 1-Ad) (1-Ad, 1-Ad, 1-Ad, 1-Ad) 一减去目标颜色的 Alpha 值
gl.SRC_ALPHA_SATURATE (min(As, 1-Ad), min(As, 1-Ad), min(As, 1-Ad), 1) (源 Alpha 饱和) 仅能作为源因子使用,目标因子通常为 gl.ONEgl.ZERO 源 Alpha 饱和因子,常用于粒子效果等,确保颜色不会过度饱和。

其中,(Rs, Gs, Bs, As) 代表源颜色 (Source Color) 的 RGBA 分量,(Rd, Gd, Bd, Ad) 代表目标颜色 (Destination Color) 的 RGBA 分量。

常用混合函数组合:

  • Alpha 混合 (Alpha Blending, 透明度混合): 这是最常用的混合模式,用于实现透明和半透明效果。

    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    • 源因子: gl.SRC_ALPHA (源颜色的 Alpha 值)

    • 目标因子: gl.ONE_MINUS_SRC_ALPHA (一减去源颜色的 Alpha 值)

    • 混合公式: 最终颜色 = 源颜色 * 源Alpha + 目标颜色 * (1 - 源Alpha)

    这种混合模式的效果是,源颜色的透明度越高,源颜色在最终颜色中的贡献就越小,目标颜色(背景颜色)的贡献就越大。

  • 加法混合 (Additive Blending): 用于实现发光、光晕、火焰等效果。

    gl.blendFunc(gl.ONE, gl.ONE);
    • 源因子: gl.ONE (1)

    • 目标因子: gl.ONE (1)

    • 混合公式: 最终颜色 = 源颜色 * 1 + 目标颜色 * 1 = 源颜色 + 目标颜色

    加法混合会将源颜色和目标颜色直接相加,颜色值会叠加,从而产生更亮的效果。

  • 乘法混合 (Multiplicative Blending): 用于实现阴影、变暗等效果。

    gl.blendFunc(gl.DST_COLOR, gl.ZERO); // 或者 gl.blendFunc(gl.ZERO, gl.SRC_COLOR);
    • 源因子: gl.DST_COLOR (目标颜色)

    • 目标因子: gl.ZERO (0)

    • 混合公式: 最终颜色 = 源颜色 * 目标颜色 + 目标颜色 * 0 = 源颜色 * 目标颜色

    乘法混合会将源颜色和目标颜色相乘,颜色值会变暗。

2.8.2.2 混合方程 (gl.blendEquation)

gl.blendEquation(mode) 函数用于设置混合操作,即如何将源颜色、目标颜色以及混合因子组合起来。

mode 参数可以是以下常量之一:

常量名称 混合方程 描述
gl.FUNC_ADD 最终颜色 = 源颜色 * SrcFactor + 目标颜色 * DstFactor 加法混合 (默认值)
gl.FUNC_SUBTRACT 最终颜色 = 源颜色 * SrcFactor - 目标颜色 * DstFactor 减法混合 (源颜色减去目标颜色)
gl.FUNC_REVERSE_SUBTRACT 最终颜色 = 目标颜色 * DstFactor - 源颜色 * SrcFactor 反向减法混合 (目标颜色减去源颜色)
gl.MIN 最终颜色 = min(源颜色, 目标颜色) 取最小值,选择源颜色和目标颜色中较小的颜色分量
gl.MAX 最终颜色 = max(源颜色, 目标颜色) 取最大值,选择源颜色和目标颜色中较大的颜色分量

默认混合方程是 gl.FUNC_ADD (加法混合)。 在大多数情况下,我们使用加法混合就足够了,例如实现透明度混合、加法混合特效等。减法混合、反向减法混合、MIN 和 MAX 混合方程在一些特殊效果中可能会用到,但相对较少。

启用和禁用混合

混合默认是禁用的。要启用混合,需要调用 gl.enable(gl.BLEND)。 要禁用混合,调用 gl.disable(gl.BLEND)

// 启用混合 gl.enable(gl.BLEND); // 设置混合函数 (例如 Alpha 混合) gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // 设置混合方程 (默认是 gl.FUNC_ADD,通常不需要显式设置) // gl.blendEquation(gl.FUNC_ADD); // ... 执行绘制操作 ... // 禁用混合 (如果需要恢复默认行为) gl.disable(gl.BLEND);

混合顺序 (Draw Order) 的重要性

当启用混合时,绘制顺序非常重要,特别是对于 透明或半透明物体

  • 不透明物体: 不透明物体可以按照任意顺序绘制,因为它们会完全遮挡后面的物体。

  • 透明/半透明物体: 透明或半透明物体必须 从后往前 绘制 (back-to-front rendering)。

为什么需要从后往前绘制透明物体?

考虑 Alpha 混合的公式: 最终颜色 = 源颜色 * 源Alpha + 目标颜色 * (1 - 源Alpha)

如果先绘制前面的透明物体,再绘制后面的透明物体,那么后面的物体的混合就会基于前面物体已经混合过的颜色,导致混合结果不正确。

只有从后往前绘制,才能保证每个透明物体的混合都基于正确的背景颜色(即之前绘制的物体或背景颜色)。

深度测试与混合:

通常情况下,我们会同时启用深度测试 (gl.enable(gl.DEPTH_TEST)) 和混合 (gl.enable(gl.BLEND))。

  • 深度测试 用于解决不透明物体的遮挡关系,确保前面的不透明物体遮挡后面的不透明物体。

  • 混合 用于处理透明和半透明物体的颜色混合。

对于透明物体,虽然需要从后往前绘制以保证正确的混合顺序,但仍然需要启用深度测试,以确保透明物体不会遮挡前面的不透明物体,并且透明物体自身的前后部分也能够正确遮挡。

总结混合流程:

2.8.3 代码实践:WebGL 颜色和混合示例

下面通过代码示例演示如何在 WebGL 中使用颜色和混合。

示例 1: 绘制两个重叠的彩色矩形 (不混合)

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>WebGL Color Example (No Blending)</title> </head> <body> <canvas id="webglCanvas" width="400" height="300"></canvas> <script> const canvas = document.getElementById('webglCanvas'); const gl = canvas.getContext('webgl'); if (!gl) { alert('WebGL not supported'); return; } // 顶点着色器 const vertexShaderSource = ` attribute vec4 a_position; attribute vec4 a_color; varying vec4 v_color; void main() { gl_Position = a_position; v_color = a_color; } `; // 片元着色器 const fragmentShaderSource = ` precision mediump float; varying vec4 v_color; void main() { gl_FragColor = v_color; } `; // 创建着色器程序 const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); // 获取属性位置 const positionAttributeLocation = gl.getAttribLocation(program, 'a_position'); const colorAttributeLocation = gl.getAttribLocation(program, 'a_color'); // 矩形顶点数据和颜色数据 (两个矩形,颜色不同) const positions = new Float32Array([ // 第一个矩形 (红色) -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, // 第二个矩形 (绿色,稍微偏移) -0.2, -0.2, 0.8, -0.2, 0.8, 0.8, -0.2, 0.8, ]); const colors = new Float32Array([ // 第一个矩形 (红色) 1.0, 0.0, 0.0, 1.0, // Red 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // 第二个矩形 (绿色) 0.0, 1.0, 0.0, 1.0, // Green 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, ]); // 创建顶点位置缓冲区 const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); // 创建顶点颜色缓冲区 const colorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); // 设置顶点属性指针 (位置) gl.enableVertexAttribArray(positionAttributeLocation); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0); // 设置顶点属性指针 (颜色) gl.enableVertexAttribArray(colorAttributeLocation); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.vertexAttribPointer(colorAttributeLocation, 4, gl.FLOAT, false, 0, 0); // 清空画布 gl.clearColor(0.0, 0.0, 0.0, 1.0); // Black background gl.clear(gl.COLOR_BUFFER_BIT); // 绘制第一个矩形 (红色) gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // 绘制第二个矩形 (绿色) gl.drawArrays(gl.TRIANGLE_FAN, 4, 4); </script> </body> </html>

在这个示例中,我们绘制了两个矩形,一个红色,一个绿色,它们在屏幕上重叠。由于 没有启用混合,后绘制的绿色矩形会完全覆盖先绘制的红色矩形在重叠区域的颜色。

示例 2: 绘制两个重叠的半透明矩形 (Alpha 混合)

修改上面的代码,启用 Alpha 混合,并将矩形的 Alpha 值设置为 0.5 (半透明)。

// ... (之前的代码,顶点着色器、片元着色器、程序创建等不变) ... // 矩形顶点数据和颜色数据 (两个矩形,半透明颜色) const colors = new Float32Array([ // 第一个矩形 (半透明红色) 1.0, 0.0, 0.0, 0.5, // Semi-transparent Red 1.0, 0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.5, // 第二个矩形 (半透明绿色) 0.0, 1.0, 0.0, 0.5, // Semi-transparent Green 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, 0.0, 0.5, ]); // ... (创建缓冲区、设置顶点属性指针代码不变) ... // 清空画布 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // **启用混合** gl.enable(gl.BLEND); // **设置 Alpha 混合函数** gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // 绘制第一个矩形 (半透明红色) gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // 绘制第二个矩形 (半透明绿色) gl.drawArrays(gl.TRIANGLE_FAN, 4, 4); // **禁用混合 (可选,如果后续绘制不需要混合)** // gl.disable(gl.BLEND);

在这个示例中,我们修改了颜色数据的 Alpha 值,并将 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) 添加到绘制代码中。 现在,重叠区域会显示红色和绿色的混合效果,呈现出一种黄绿色。这是因为使用了 Alpha 混合,半透明的绿色矩形与半透明的红色矩形进行了混合。

示例 3: 加法混合 (Additive Blending) 示例

gl.blendFunc 修改为加法混合模式:

// ... (之前的代码,顶点着色器、片元着色器、程序创建、顶点数据等不变) ... // ... (创建缓冲区、设置顶点属性指针代码不变) ... // 清空画布 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 启用混合 gl.enable(gl.BLEND); // **设置加法混合函数** gl.blendFunc(gl.ONE, gl.ONE); // 加法混合 // 绘制第一个矩形 (红色) gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // 绘制第二个矩形 (绿色) gl.drawArrays(gl.TRIANGLE_FAN, 4, 4); // 禁用混合 (可选) // gl.disable(gl.BLEND);

在这个示例中,我们使用了 gl.blendFunc(gl.ONE, gl.ONE) 设置为加法混合。 重叠区域的颜色会变成亮黄色,因为红色和绿色相加,颜色值叠加,变得更亮。加法混合常用于创建发光效果。

2.8.4 总结

颜色和混合是 WebGL 图形渲染中不可或缺的部分。理解颜色表示方法和混合原理,并灵活运用 gl.blendFunc()gl.blendEquation() 函数,可以实现各种复杂的视觉效果。

  • 颜色表示: WebGL 使用 RGBA 模式,颜色分量值范围为 0.0 到 1.0。

  • 混合原理: 将源颜色和目标颜色按照混合函数和混合方程进行组合。

  • 混合函数 (gl.blendFunc): 设置源混合因子和目标混合因子,决定源颜色和目标颜色在混合中的权重。

  • 混合方程 (gl.blendEquation): 设置混合操作,决定如何组合源颜色、目标颜色和混合因子。

  • 常用混合模式: Alpha 混合 (透明度)、加法混合 (发光)、乘法混合 (阴影) 等。

  • 混合顺序: 透明物体需要从后往前绘制。

  • 启用/禁用混合: 使用 gl.enable(gl.BLEND)gl.disable(gl.BLEND) 控制混合的开关。

通过掌握颜色和混合技术,可以为 WebGL 应用增添更多视觉层次和表现力,创造出更生动、更吸引人的图形效果。 希望本章的详细讲解和代码示例能够帮助你深入理解 WebGL 中的颜色和混合概念,并在实际项目中灵活应用。


发布者: 作者: 转发
评论区 (0)
U