WebGL - 初相识 - 着色器

前言

书接上回,这次会着重聊一聊上一篇文章中最后的例子中所用到的 WebGL 着色器,至于示例代码就不再复制了,可以通过上面的链接去回顾一下。

着色器

着色器就是让开发者自己编写一段程序代码(GLSL),用来替代固定渲染管线来处理图像的渲染。

在 WebGL 中,着色器分为两种:顶点着色器 和 片元着色器。

想要创建一个着色器还是有点麻烦的,需要使用 GLSL 编写顶点着色器源码,然后使用 WebGL 上下文对象 WebGLRenderingContext 也就是 canvas.getContext('webgl') ,去创建一个顶点着色器类型的实例,创建完实例后还需要进行源码绑定,最后还需要编译着色器才行。

实际上有更多其他类型着色器,例如:几何着色器和计算着色器,但是这几类着色器并不包含在标准规范中,因此 WebGL 不直接支持这些着色器,但是可以通过扩展机制,在特定的设备和浏览器中使用,当然前提是需要确保设备和浏览器支持所需的功能。

顶点着色器

初步可以理解为:坐标位置、大小等

顶点着色器是用于对三维模型的顶点进行处理和变换的程序,它在图形渲染管线的早期阶段运行,主要负责的任务是将输入的顶点坐标和其他属性(如法向量、纹理坐标等)转换成屏幕坐标,并进行一些其他的操作,如光照计算、变形、筛选等,还可以根据需要对顶点属性进行插值计算,将结果传递给下一个阶段的图形渲染管线。

片元着色器

初步可以理解为:颜色、皮肤等

片元着色器是一个计算和处理每个像素的小程序,也被称为像素着色器。它接收由顶点着色器传递的数据,如顶点的颜色、法线或纹理坐标等。片元着色器根据给定的光照模型、纹理等信息,计算每个像素的最终颜色。它可以根据各种算法和技术,实现逼真的光照效果、纹理映射、阴影和特殊效果等。

创建顶点着色器

获取上下文的方法在本中后续的例子中也会省略掉!

在 WebGL 上下文对象中,可以使用 createShader 方法来创建着色器,该方法接收一个参数 type ,也就是着色器类型,参数 typectx.VERTEX_SHADERctx.FRAGMENT_SHADER 中的一个(ctx 指的是 WebGL 上下文对象),其中 ctx.VERTEX_SHADER 代表顶点着色器类型,ctx.FRAGMENT_SHADER 代表片元着色器类型。

javascript 复制代码
// 获取上下文的方法在本中后续的例子中也会省略掉!
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('webgl')

// 创建着色器
const vertexShader = ctx.createShader(ctx.VERTEX_SHADER) // 顶点着色器
const fragmentShader = ctx.createShader(ctx.FRAGMENT_SHADER) // 片元着色器

指定着色器源码

光创建了着色器实例还不够的,还需对着色器实例设置其程序代码。

需要使用 shaderSource 方法来设置着色器的 GLSL 程序代码,该方法接收两个入参 shader, source,其中 shader 是着色器实例,source 是 GLSL 程序代码字符串。

这里稍微介绍一下 GLSL ,GLSL(OpenGL Shading Language)是用于编写着色器代码的语言,它类似于C语言。

GLSL 是一种强类型语言,支持的数据类型有 int、float、bool以及矢量类型(vec2、vec3、vec4);矢量类型表示具有多个分量的值,比如 vec3 表示具有3个浮点分量的矢量。

GLSL 提供了一些内置的特殊变量和函数,用于访问着色器程序的输入和输出,以及执行常见的数学和图形操作。例如,gl_Position 表示顶点着色器的输出位置、gl_PointSize 表示绘制点的大小、gl_FragColor 表示片元着色器的输出颜色。

csharp 复制代码
// 顶点着色器
const vertexShaderSource = `
    void main () {
        // 要绘制点的坐标
        // 这里的入参分别代表 x, y, z, w
        // 其中 w 代表的是齐次坐标
        // 下面的参数最终可以理解成 x/w, y/w, z/w
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0); 
        
        // 点的大小
        gl_PointSize = 10.0; 
    }
`

// 片元着色器
const fragmentShaderSource = `
    void main () {
        // 要绘制的颜色信息
        // 这里的入参分别代表 r, g, b, a
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
    }
`

// 指定着色器的代码
ctx.shaderSource(vertexShader, vertexShaderSource) // 设置顶点着色器源码
ctx.shaderSource(fragmentShader, fragmentShaderSource) // 设置片元着色器源码

编译着色器

接着需要使用 compileShader 方法来对着色器进行编译操作,使其成为 二进制数据,才可以被 WebGLProgram(着色器程序对象) 对象所使用。

compileShader 方法接受一个参数 shader,即着色器对象。

scss 复制代码
// 编译着色器 
ctx.compileShader(vertexShader) 
ctx.compileShader(fragmentShader)

着色器程序对象

还没完,目前着色器仍然无法使用,还需要创建着色器程序对象(WebGLProgram),着色器程序对象由两个着色器组成,分别是顶点着色器和片元着色器。

创建一个着色器程序对象需要使用 createProgram 方法,然后还需要使用 attachShader 方法为其 附加/绑定 上着色器,之后还需要使用 linkProgram 方法将它们连接成一个可用的程序。

createProgram 方法无需入参。
attachShader 方法接收两个参数 program(色器程序对象)、shader(着色器对象)。
linkProgram 方法接收一个参数 program ,即着色器程序对象。

scss 复制代码
// 创建程序对象
const program = ctx.createProgram()

// 绑定着色器
ctx.attachShader(program, vertexShader)
ctx.attachShader(program, fragmentShader)

// 将顶点着色器和片元着色器连接成一个可执行的程序
ctx.linkProgram(program)

绘制

目前我们拥有一个可用的着色器程序对象,那么是否可以直接进行绘制了呢?答案是 不可以

在绘制之前,我们需要使用 useProgram 方法将定义好的着色器程序对象添加到当前的渲染状态中,然后才可以执行 drawArrays 方法进行最终的绘制了。

useProgram 方法接收一个参数 program ,即着色器程序对象。
drawArrays 方法接收三个参数,分别是:mode、first、count;

  • mode 用来指定绘制图元的方式,可能值如下:
    • POINTS(点)
    • LINE_STRIP(线条)
    • LINE_LOOP(线圆)
    • LINES(线段)
    • TRIANGLE_STRIP(三角带)
    • TRIANGLE_FAN(三角扇)
    • TRIANGLES(三角形)
  • first 指定从那个点开始绘制;
  • count 指定绘制需要使用到多少个点;
scss 复制代码
// 将指定的程序对象设置为当前绘制的程序,可以理解为注册的意思?
ctx.useProgram(program)

// 执行绘制 
// 入参分别是 要绘制的基本图元类型、从哪个顶点开始绘制、要绘制的顶点数量
ctx.drawArrays(ctx.POINTS, 0, 1)

总结

到这里我们已经初步的了解到了着色器,知道了如何去创建着色器并且将着色器绘制到当前的画布中。当然整个过程还是挺复杂的,要用到的 API 也还挺多的,不过相信还是难不倒 jym 。

相关推荐
mldong3 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排4 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60614 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角5 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!5 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰6 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi7 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen8 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马8 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒9 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端