颜色与纹理,纹理颜色原理

2020-02-02 作者:网站首页   |   浏览(80)

时间: 2019-01-29阅读: 399标签: WebGL导语

本章主要内容:
(1) 将顶点的其他(非坐标)数据 —— 如颜色等 —— 传入顶点着色器
(2)发生在顶点着色器和片元着色器之间的从图形到片元的转化,又称图元光栅化(rasterzation process)
(3)将图形(或称纹理)映射到图形或三维对象的表面上。

WebGL绘制图像时,往着色器中传入颜色信息就可以给图形绘制出相应的颜色,现在已经知道顶点着色器和片段着色器一起决定着向颜色缓冲区写入颜色信息并最终呈现出来,那么这个过程是什么样,如果图形的颜色需要用现有图片来渲染那么又该如何操作?

  1. 将顶点坐标传入着色器,步骤:
    (1)创建缓冲区对象。
    (2)将缓冲区对象绑定到tartget上。
    (3)将顶点坐标数据写入缓冲区对象。
    (4)将缓冲区对象分配给对应的 attribute 变量。
    (5)开启 attribute 变量。

颜色缓冲区

yzc216亚洲城 1

在绘制开始前,经常见到调用函数清空画布的代码gl.clear(gl.COLOR_BUFFER_BIT),清空画布的绘图区实际上就是用之前定义好的背景颜色将颜色缓冲的的颜色清除。颜色缓冲区中存放着需要显示到画布上的像素的颜色数据,它属于帧缓存的一部分,与深度缓存、模板缓存等一起决定着最终画布上图像的显示信息。

使用两个缓冲区对象向顶点着色器传输数据

可以将颜色缓存区看成图像颜色存储器,在缓存区中以RGB或RGBA的格式存储着画布上每一个像素的颜色信息,各个像素点组合起来就构成了颜色缓存的矩形阵列。这个定义看起来与图片存储器是很相似的,颜色缓存为RGB或是RGBA每一个通道分配存放位数,其中RGB就是颜色数据,A表示alpha也就是该像素的透明度信息,颜色占用的位数值就是颜色深度,比如颜色深度为24位,表示每一个像素24位,一般24位的分配方案就是红色、蓝色、绿色各占8位,如果需要透明效果的话,可以采用32位颜色深度为alpha通道分配8位。

  1. 交错组织(interleaving):将顶点坐标尺寸数据打包到同一缓冲区对象中。

  2. gl.vertexAttribPointer()

这里可以总结得出,画布上各个像素点呈现的颜色就是存放在颜色缓冲区的颜色信息所决定的,而绘制图形的颜色缓冲区的信息又是由顶点着色器决定。要知道颜色如何渲染就要深入分析着色器的工作过程。

yzc216亚洲城 2

图形装配

gl.vertexAttribPointer()

要绘制一个三角形,我们是这样定义着色器的:

  1. varying 变量: 从顶点着色器向片元着色器中传入数据。
    varying 变量只能是float(以及相关的vec2,vec3,vec4,mat2,mat3,mat4)。

  2. 在顶点着色器和片元着色器之间,有两个步骤:
    [1] 图形装配过程:将孤立的顶点坐标装配成几何图形。几何图形的类别由gl.drawArray() `函数的第一个参数决定。
    输入数据:gl_Position
    [2] 光栅化过程:将装配好的几何图形转为片元。

// 顶点着色器const VSHADER_SOURCE = `attribute vec4 a_Position; void main() { gl_Position = a_Position; }`;// 片段着色器const FSHADER_SOURCE = `void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`;

yzc216亚洲城 3

之后通过gl.program将顶点position坐标传入顶点着色器,这就相当于在画布上确定了几个点的坐标信息,这些点需要用线条连接起来才能构成图形,这个由顶点坐标装配成几何图形的过程就叫做图形装配。

顶点着色器和片元着色器之间的图形装配和光栅化

被装配的基本图形被称作图元,它包含点、线、面等基本几何图形。在调用WebGL的drawArrays或drawElements方法时作为参数传入,从而指定图元类型。

步骤:
(1)根据 gl.drawArrays()的参数n,将执行顶点着色器n次(执行顶点着色器结果:将坐标传入并存储在装配区)
(2)开始装配图形。使用传入的点坐标,根据 gl.drawArrays()的第一个参数信息(如:gl.TRANGLES)来决定如何装配。
(3)光栅化

一个三角形的绘制过程拆分来看就是执行三次顶点着色器,将三个点坐标都传入装配区,根据绘制函数的图元参数gl.TRIANGLES将三个点装配成三角形,然后进入下一个过程——光栅化。

yzc216亚洲城 4

光栅化

几何图形装配和光栅化过程

简单来说,光栅化就是将图形转化成片元,可以理解成一个个像素。只有将图形转化成像素后才能交由片段着色器处理。

  1. 颜色内插(interpolation)

光栅化结束后,WebGL执行片段着色器。每执行一次片段着色器就处理一个片元,将该片元的颜色写入颜色缓冲区中,等到图形中所有的片元处理完毕画布上就得到了最后的图像。

yzc216亚洲城 5

如上面的例子,每一个片元都会被执行成红色,由这一个个红色像素组成的三角形也就是红色的。

颜色值的内插

如果要绘制一个多颜色三角图形又是一个什么过程呢?首先需要修改着色器的定义,也许可以这样:

  1. 纹理映射(texture mapping):根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色。
    纹素(texels, texture elements):组成纹理图像的像素。每一个纹素的颜色都是用RGBRGBA格式编码。
// 顶点着色器const VSHADER_SOURCE = `attribute vec4 a_Position; attribute vec4 a_Color; varying vec4 v_Color; void main() { gl_Position = a_Position; v_Color = a_Color; }`;// 片段着色器const FSHADER_SOURCE = `varying vec4 v_Color; void main() { gl_FragColor = v_Color; }`;

yzc216亚洲城 6

向顶点着色器传入顶点坐标和颜色两个数据,执行三次后得到三角形三个顶点的坐标和颜色,接下来通过图元装配得到一个三角形的图元,到了关键的光栅化这一步,该如何定义片元的颜色呢?WebGL采用一个叫做内插的过程来计算颜色的值。

纹素

以一条线为例来解释内插,两个端点分别为(1.0,0.0,0.0)和(0.0,1.0,0.0),从一端到另一端,R的值从1.0降到0.0,G的值由0.0升到1.0,线上的所有点颜色值都这样计算出来,实现了平滑的颜色渐变,这就是内插。

  1. 在WebGL中,要进行纹理映射,需遵循以下四步:
    (1)准备好映射到几何图形上的纹理图像。
    (2)为几何图形配置纹理映射方式。
    (3)加载纹理图像,对其进行一些配置,以在WebGL中使用它。
    (4)在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元。

  2. 纹理坐标
    纹理坐标是纹理图像上的坐标,通过纹理坐标可以在纹理图像上获取纹素颜色。
    WebGL系统中的纹理坐标系统是二维的。
    WebGL中使用s和t命名纹理坐标。(st坐标系统)

经过内插,图形的每一个片元都指定了自己的颜色,写入颜色缓冲区后呈现出来。

yzc216亚洲城 7

纹理贴图

WebGL的纹理坐标系统

如果要为WebGL创建更加复杂更加自然的现实效果,就需要采用贴图来将现成的图片贴到图形上。

  1. 将纹理图像粘贴到几何图形上
    在WebGL中,我们通过纹理图像的纹理坐标与几何图形顶点坐标间的映射关系,来确定怎样将纹理图像贴上去。

图片容器中存放的也是一个个RGB或RGBA的像素,将图片的信息读取后存放在纹理对象或者说纹理图像中,纹理图像有自己的坐标系,坐标中每一个单元格就存放的纹理图像的像素信息,也被称作纹素。

yzc216亚洲城 8

将纹理图像的坐标转换到画布上图形的坐标的映射过程就是纹理映射,这个过程中,为图形顶点指定了纹理坐标,剩下的颜色由内插计算得出,写入颜色缓冲区后,图形的表面就被贴上了图像的颜色。

将纹理图像映射到顶点上

用一个案例来实现纹理贴图,现在要做的是:

  1. 纹理映射过程详解
    步骤:
    (1)顶点着色器中接收定点的纹理坐标,光栅化后传递给片元着色器。
    (2)片元着色器根据片元的纹理坐标,从纹理图像中抽取出纹素颜色,赋给当前片元。
    (3)设置顶点的纹理坐标(initVertexBuffers()

加载好需要的纹理图像设置纹理坐标对纹理进行配置片段着色器抽出纹素并赋值给片元

在这个例子中我选择提前加载图片。在这里要注意有的浏览器不允许访问本地文件,可以考虑自己搭建server或是开启浏览器访问本地文件。

var verticesTexCoords = new Float32Array([
  -0.5,  0.5,   0.0, 1.0,
  -0.5, -0.5,   0.0, 0.0,
   0.5,  0.5,   1.0, 1.0,
   0.5, -0.5,   1.0, 0.0,    
]) 
function main() { const canvas = document.getElementById('webgl'); const webgl = getWebGLContext(canvas); webgl.images = {}; // 初始化之前先加载图片 loadImage([ `src/images/0.jpeg`, ], webgl).then((gl) = { if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to intialize shaders.'); return; } gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); const n = initVertexBuffers(gl); initTextures(gl, n, 0); });}

将顶点坐标和纹理坐标写入缓冲区对象,将其中的顶点坐标分配给a_yzc216亚洲城,Position变量并开启之。

loadImage的实现很简单,用一个promise来处理异步加载图片,传入数组为了之后支持多张图片。在initVertexBuffers中创建数据buffer,将图形顶点和纹理图像坐标一起传入着色器。

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position);
function initVertexBuffers(gl) { // 顶点坐标和纹理图像坐标 const vertices = new Float32Array([ -0.3, 0.7, 0.0, 0.0, 1.0, -0.3, -0.7, 0.0, 0.0, 0.0, 0.3, 0.7, 0.0, 1.0, 1.0, 0.3, -0.7, 0.0, 1.0, 0.0, ]); const FSIZE = vertices.BYTES_PER_ELEMENT; const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); const a_Position = gl.getAttribLocation(gl.program, 'a_Position'); const a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord'); gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 5, 0); gl.enableVertexAttribArray(a_Position); gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 5, FSIZE * 3); gl.enableVertexAttribArray(a_TexCoord); return 4;}

获取a_TexCoord变量的存储位置,将缓冲区中的纹理坐标分配给该变量更开启。

然后看看最主要的initTextures,在这里配置纹理:

var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord);
function initTextures(gl, n, index) { // 创建纹理对象 const texture = gl.createTexture(); const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); const image = gl.images[index]; // WebGL纹理坐标中的纵轴方向和PNG,JPG等图片容器的Y轴方向是反的,所以先反转Y轴 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 激活纹理单元,开启index号纹理单元 gl.activeTexture(gl[`TEXTURE${index}`]); // 绑定纹理对象 gl.bindTexture(gl.TEXTURE_2D, texture); // 配置纹理对象的参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // 将纹理图像分配给纹理对象 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); // 将纹理单元编号传给着色器 gl.uniform1i(u_Sampler, index); // 绘制 gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);}

(4)准备待加载的纹理图像,令浏览器读取它(initTextures())
initTextures() => 配置和加载问题
调用gl.createTexture()创建纹理对象

这里又遇到两个概念:

var texture = gl.createTexture();

纹理对象配置参数 texParameteri方法用来配置纹理对象参数,函数第二个参数传入配置参数名,第三个参数传入配置参数值,可以配置的参数有:

yzc216亚洲城 9

伸展(gl.TEXTURE_MAX_FILTER): 绘制图形比纹理图像大的时候怎么取纹素,默认值gl.LINEAR收缩(gl.TEXTURE_MIN_FILTER): 绘制图形比纹理图像小的时候怎么取纹素, 默认值gl.NEAREST_MIP_LINEAR水平填充(gl.TEXTURE_WRAP_S): 定义绘制图形水平方向如何填充,默认值gl.REPEAT垂直填充(gl.TEXTURE_WRAP_T): 定义绘制图形垂直方向如何填充,默认值gl.REPEAT

gl.createTexture()

详细参考texParameteri

同样,也可以使用gl.deleteTeture()来删除一个纹理对象。如果试图删除一个已经被删除的纹理对象,不会报错也不会产生任何影响。

纹理单元 如果需要使用多张图片就要管理多个纹理图片,WebGL为了使用多个纹理,用纹理单元来处理纹理图像。WebGL的实现至少支持8个纹理单元,分别用gl.TEXRTRUE0,gl.TEXRTRUE1,...,gl.TEXRTRUE7来表示。

yzc216亚洲城 10

最后是着色器代码,在调用gl.drawArrays传入图元类型TRIANGLE_STRIP后执行:

gl.deleteTeture()

const VSHADER_SOURCE = `attribute vec4 a_Position; attribute vec2 a_TexCoord; varying vec2 v_TexCoord; void main() { gl_Position = a_Position; v_TexCoord = a_TexCoord; }`;const FSHADER_SOURCE = `precision mediump float; uniform sampler2D u_Sampler; varying vec2 v_TexCoord; void main() { gl_FragColor = texture2D(u_Sampler, v_TexCoord); }`;

调用gl.getUniformLocation()从片元着色器中获取uniform变量u_Sampler(取样器)的存储位置,该变量用来接收纹理图像

顶点着色器中传入纹理图像的顶点坐标,将它传递给片段着色器,在片段着色器中声明了一个专用于纹理对象的数据类型sampler2D,指向一个纹理单元编号(接下来解释),着色器获取纹素由函数texture2D完成,传入参数纹理单元编号和纹理图像坐标。

var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');

多纹理实现

创建image对象

要使用多个纹理就要用到更多的纹理单元,多个纹理可以组合也可以单独渲染,利用前面的代码,可以很容易扩展成一起多纹理的案例,加上一些3D效果和动画,就可以组合成一个轮播图片。

var image = new Image();

本文由云 社区发表,作者:ivweb qcyhust

注册图像加载事件的响应函数

image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };

浏览器开始加载图像

image.src = '../resources/sky.jpg';

(5)监听纹理图像的加载事件,一旦加载完成,就在WebGL系统中使用纹理(loadTexture())。

loadTexture() => 配置纹理供WebGL使用

对纹理图像进行y轴翻转(WebGL纹理坐标中的t轴的方向和PNG、BMP、JPG等格式图片的坐标系统的Y轴方向是相反的。)

gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

yzc216亚洲城 11

图像坐标系统和WebGL纹理坐标系统

yzc216亚洲城 12

gl.pixelStorei()

开启0号纹理单元
WebGL通过纹理单元的机制来同时使用多个纹理。
每个纹理单元有一个单元编号来管理一张纹理图像。
系统支持的纹理单元格数取决于硬件和浏览器的WebGL实现,但是在默认情况下,WebGL至少支持8个纹理单元。
在使用纹理单元之前,还需要调用gl.aactiveTexture()来激活它。

gl.activeTexture(gl.TEXTURE0);

yzc216亚洲城 13

gl.activeTexture()

绑定纹理对象(设置纹理的类型)

gl.bindTexture(gl.TEXTURE_2D, texture);

yzc216亚洲城 14

纹理类型

yzc216亚洲城 15

gl.bindTexture()

yzc216亚洲城 16

将纹理坐标绑定到目标上

配置纹理对象的参数 => 设置纹理图像映射到图形上的具体方式

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

yzc216亚洲城 17

gl.texParameteri()

yzc216亚洲城 18

param 参数值

yzc216亚洲城 19

四种纹理参数及它们锁产生的效果

yzc216亚洲城 20

纹理参数及它们的默认值

yzc216亚洲城 21

配置纹理图像 => 将纹理图像分配给纹理对象,同时允许你告诉WebGL系统关于该图像的一些特性。

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);

yzc216亚洲城 22

gl.texImage2D()

yzc216亚洲城 23

纹素数据的数据格式

format标示纹理数据的格式,必须根据纹理图像的格式来选择这个参数
JPG => gl.RGB
PNG => gl.RGBA
BMP => gl.RGB
gl.LUMUNANCEgl.LUMINANCE_ALPHA 通常用在灰度图像上等等
流明(luminance)标示我们感知到的物体表面的亮度。通常使用物体表面红、蓝颜色分量指的加权平均来计算流明。

yzc216亚洲城 24

纹理数据的数据格式

type参数制定了纹理数据类型。通常使用gl.UNSIGNED_BYTE数据类型。

分配后的WebGL系统:

yzc216亚洲城 25

将图像分配给纹理对象

将0号纹理传递给着色器中的取样器变量

gl.uniform1i(u_Sampler, 0);

uniform sampler2D u_Sampler;

yzc216亚洲城 26

专用于纹理的数据类型

分配给unform变量后的WebGL系统:

yzc216亚洲城 27

将纹理单元分配给uniform变量

绘制矩形

gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);

从顶点着色器向片元着色器传输纹理坐标

var VSHADER_SOURCE =
  'attribute vec4 a_Position;n'  
  'attribute vec2 a_TexCoord;n'  
  'varying vec2 v_TexCoord;n'  
  'void main() {n'  
  '  gl_Position = a_Position;n'  
  '  v_TexCoord = a_TexCoord;n'  
  '}n';

在片元着色器中获取纹理像素颜色(texture2D())

 gl_FragColor = texture2D(u_Sampler, v_TexCoord);

yzc216亚洲城 28

texture2D()

本文由yzc216亚洲城发布于网站首页,转载请注明出处:颜色与纹理,纹理颜色原理

关键词: yzc216亚洲城