首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >WebGL 纹理颜色原理

WebGL 纹理颜色原理

原创
作者头像
腾讯IVWEB团队
修改于 2017-11-09 01:43:44
修改于 2017-11-09 01:43:44
2.8K0
举报

本文作者:ivweb qcyhust 原文出处:IVWEB社区 未经同意,禁止转载

导语

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

颜色缓冲区

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

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

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

图形装配

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

代码语言:txt
AI代码解释
复制
// 顶点着色器
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);
  }`;

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

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

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

光栅化

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

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

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

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

代码语言:txt
AI代码解释
复制
// 顶点着色器
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;
  }`;

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

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

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

纹理贴图

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

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

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

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

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

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

代码语言:txt
AI代码解释
复制
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);
  });
}

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

代码语言:txt
AI代码解释
复制
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;
}

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

代码语言:txt
AI代码解释
复制
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);
}

这里又遇到两个概念:

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

  • 伸展(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

详细参考texParameteri

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

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

代码语言:txt
AI代码解释
复制
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);
  }`;

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

多纹理实现

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WebGL简易教程(十一):纹理
在之前的之前的教程《WebGL简易教程(九):综合实例:地形的绘制》中,绘制了一个带颜色的地形场景。地形的颜色是根据高程赋予的RGB值,通过不同的颜色来表示地形的起伏,这是表达地形渲染的一种方式。除此之外,还可以将拍摄得到的数字影像,贴到地形上面,得到更逼真的地形效果。这就要用到我们这一章的新知识——纹理了。
charlee44
2019/10/18
1.2K0
WebGL简易教程(十一):纹理
一起学 WebGL:纹理对象学习
纹理对象,是将像素(texels)以数组方式传给 GPU 的对象,常见场景是贴图,就是将图片的数据应用到 3D 物体上。
前端西瓜哥
2023/08/18
3350
一起学 WebGL:纹理对象学习
webgl 基础
WebGL仅仅是一个光栅化引擎,它可以根据你的代码绘制出点,线和三角形。 WebGL在电脑的GPU中运行,每对方法中一个叫顶点着色器, 另一个叫片断着色器,并且使用一种和C或C++类似的强类型的语言 GLSL。 每一对组合起来称作一个 program(着色程序)
ronixiao
2022/08/08
1.5K0
【前端er入门Shader系列】04—MVP矩阵与纹理映射
我们生活在三维的由各种色彩构成的大千世界里,包含了复杂的细节纹理,而纹理采样作为重要的图形学技术,能够在渲染物体表面时,使用一张图片来提供颜色信息,从而增强物体的细节和真实感。本章将结合少量数学知识,介绍如何使用MVP矩阵将三维场景投射到二维屏幕,最终执行纹理采样的过程。
CS逍遥剑仙
2025/01/13
3720
WebGL简易教程(十四):阴影
所谓阴影,就是物体在光照下向背光处投下影子的现象,使用阴影技术能提升图形渲染的真实感。实现阴影的思路很简单:
charlee44
2019/12/10
1.8K0
WebGL简易教程(十四):阴影
【愚公系列】2023年08月 WEBGL专题-2D特效-放大镜
放大镜是一种透明的光学镜片,经常用于放大视野中的细节。它有一个凸透镜或一个复合透镜,可以通过透镜的曲面来聚焦光线,使得物体在放大镜的另一端显得更大和更清晰。放大镜通常用于读取小字或细节,检查小物品或难以观察的事物,如昆虫、病毒等。
愚公搬代码
2025/05/28
530
【愚公系列】2023年08月 WEBGL专题-2D特效-放大镜
WebGL: 从 2D 开始
腾讯IVWEB团队
2017/10/13
5.2K0
WebGL: 从 2D 开始
WebGL简易教程(十五):加载gltf模型
一般来说,图形渲染总是需要从磁盘数据开始,最终保存到磁盘数据中,保存这种数据的就是3D模型文件。3D模型文件一般会把顶点、索引、纹理、材质等等信息都保存起来,方便下次直接读取。3D模型文件格式一般是与图形渲染工作强关联的,了解3D模型文件格式的组成,有助于进一步了解图形渲染的流程。
charlee44
2020/02/14
5.1K1
实用 WebGL 图像处理入门
技术社区里有种很有意思的现象,那就是不少人们口耳相传中的强大技术,往往因为上手难度高而显得曲高和寡。从这个角度看来,WebGL 和函数式编程有些类似,都属于优势已被论证了多年,却一直较为不温不火的技术。但是,一旦这些技术的易用性跨越了某个临界点,它们其实并没有那么遥不可及。这次我们就将以 WebGL 为例,尝试降低它的入门门槛,讲解它在前端图像处理领域的应用入门。
ConardLi
2020/02/23
3.3K0
干货 | 移动应用中使用OpenGL生成转场特效
作者简介 jzg,携程资深前端开发工程师,专注Android开发; zcc,携程高级前端开发工程师,专注iOS开发。 一、前言 随着移动端短视频的火热,音视频编辑工具在做内容类APP上的地位举足轻重。丰富的转场方式可以给短视频带来更多炫酷的效果,从而更好地赢得用户青睐。本议题主要包含了对OpenGL的简单介绍及相关API使用,GLSL着色器语言的基本使用,以及如何通过编写自定义的着色器程序来实现图片的转场效果。 二、为什么使用OpenGL以及使用的难点 2.1 为什么使用OpenGL 视频的转场效果离不开图
携程技术
2022/09/26
2K0
干货 | 移动应用中使用OpenGL生成转场特效
几个简单的小例子手把手带你入门webgl
各位同学们大家好,又到了周末写文章的时间,之前群里有粉丝提问, 就是shader不是很理解。然后今天他就来了, 废话不多说,读完今天的这篇文章你可以学到以下几点:
coder_koala
2021/09/18
1.5K0
几个简单的小例子手把手带你入门webgl
面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗
这个就引出了文本提到的全屏三角形,它不需要顶点缓冲区,而是利用顶点着色器直接生成所需的顶点坐标和纹理坐标。
字节流动
2024/06/18
3011
面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗
WebGL简易教程(四):颜色
在上一篇教程《WebGL简易教程(三):绘制一个三角形(缓冲区对象)》中,通过使用缓冲区对象(buffer object)来向顶点着色器传送数据。那么,如果这些数据(与顶点相关的数据,如法向量、颜色等)需要继续传送到片元着色器该怎么办呢?
charlee44
2019/09/29
9760
WebGL简易教程(四):颜色
【愚公系列】2022年09月 微信小程序-WebGL纹理材质的使用
在现实中webgl的用途很多,比如医院运维网站,地铁运维网站,海绵城市,可以以三维网页形式展示出现实状态。
愚公搬代码
2022/09/28
1K1
【愚公系列】2022年09月 微信小程序-WebGL纹理材质的使用
WebGL简易教程(十三):帧缓存对象(离屏渲染)
事物是普遍联系的。为了达到更加真实的渲染效果,很多时候需要利用被渲染物体在其他状态下的中间渲染结果,处理到最终显示的渲染场景中。这种中间渲染结果,就保存在帧缓冲区对象(framebuffer object,简称FBO)中,用来替代颜色缓冲区或深度缓存区。由于其结果并不直接被显示出来,所以这种技术也被称为离屏绘制(offscreen drawing)。
charlee44
2019/12/02
3K0
【WebGL】初探WebGL,我了解到这些
WebGL基于OpenGL ES(嵌入式系统) 一种广泛用于在各种平台上渲染2D和3D图形的标准。它允许开发人员使用JavaScript与用户设备的GPU(图形处理单元)交互,实现硬件加速渲染。
且陶陶
2023/10/16
5290
【WebGL】初探WebGL,我了解到这些
OpenGL 学习系列 --- 纹理
要注意到,OpenGL 绘制的物体是 3D 的,而纹理是 2D 的,那么纹理映射就是将 2D 的纹理映射到 3D 的物体上,可以想象成用一张纸裹着一个物体一样,不过要按照一定规律来。
音视频开发进阶
2019/07/25
1.6K0
NDK OpenGLES 3.0 开发(二):纹理映射
现实生活中,纹理(Texture)最通常的作用是装饰 3D 物体,它就像贴纸一样贴在物体表面,丰富了物体的表面和细节。
字节流动
2020/06/03
1.2K0
面试中经常被问到的 OpenGL ES 对象,你知道的有哪些?
VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EBO(Element Buffer Object)是指图元索引缓冲区对象,VBO 和 EBO 实际上是对同一类 Buffer 按照用途的不同称呼。
字节流动
2020/12/15
2.3K0
从关键概念开始,万字带你轻松入门 WebGL
只要理解了 WebGL 背后的概念,学习 WebGL 并没有那么难。很多 WebGL 入门文章并没有介绍这些重要的概念,直接使用 WebGL 复杂的 API 开始渲染图形,很轻松就把入坑文变成了劝退文。这篇文章将会着重讲解这些概念,并一步步探究 WebGL 是如何渲染图片到屏幕的,理解这些重要的概念,将会大大降低学习曲线。
羽月
2022/10/08
2.3K0
从关键概念开始,万字带你轻松入门 WebGL
相关推荐
WebGL简易教程(十一):纹理
更多 >
交个朋友
加入腾讯云技术交流站
洞悉AI新动向 Get大咖技术交流群
加入HAI高性能应用服务器交流群
探索HAI应用新境界 共享实践心得
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档