<十一>入门CocosShader之顶点着色器和片元着色器

创建项目

创建一个shader文件夹,在文件夹下创建一个html文件,一个js文件,两个文件都命名为shader.
image

编写代码

  1. 准备顶点数据
    在开始编写图形绘制代码前必须先给WebGL一些顶点数据
    假如需要渲染一个平面三角形,就需要输入3个顶点
    image
    因为是平面三角形,所以没有深度z,使用二维位置定义即可,以数组的形式存储。
    上图就是为绘制平面三角形定义的三个顶点坐标。
    顶点着色器的主要功能就是坐标转换,把顶点坐标转换为裁剪坐标,然后GPU会通过裁剪和透体剔除法把裁剪坐标转换为标准化设备坐标NDC。
    image
    透视剔除法会裁剪空间里三个顶点的分量都除以w,实现裁剪坐标转换为NDC,NDC的每一个分量都在(-1,1)之间,超出部分最终不会呈现,所以在游戏引擎中无论使用的是局部坐标还是世界坐标,都需要经过坐标转换,最终GPU会将NDC转换成屏幕坐标传入光栅器。
    因为这里是示例教程,所以采用的不是局部坐标或世界坐标,而是采用裁剪坐标。

有了这样一组顶点数据,就可以将它发送给图形渲染管线的第一个阶段:顶点着色器,它会在GPU上创建内存存储顶点数据,接着结合顶点组合方式来解析这些内存。WebGL通过顶点缓冲对象VBO管理这个内存,它会在GPU内存储大量的顶点数据以供顶点着色器使用。
2. 编写代码
在html中启用代码

<body>
    <script src="shader.js"></script>
    
</body>

在js中创建一个入口函数main


//创建着色器
function createShader(gl,type,source){
    //创建顶点着色器对象 | 片段着色器
    const shader = gl.createShader(type);
    //将顶点着色器文本关联到顶点着色器对象 | xxx
    gl.shaderSource(shader, source);
    //编译顶点着色器 | xxx
    gl.compileShader(shader);
    //判断编译状态(编译成功才能使用)
    const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if(success){
        return shader;
    }
    //编译失败,打印错误信息
    console.log(gl.getShaderInfoLog(shader));
    //删除顶点着色器 | xxx
    gl.deleteShader(shader);
}
//创建着色程序
function createProgram(gl, vertexShader, fragmentShader){
    //创建着色程序对象
    const program = gl.createProgram();
    //将顶点着色器关联到着色程序对象上
    gl.attachShader(program, vertexShader);
    //将片段着色器关联到着色程序对象上
    gl.attachShader(program, fragmentShader);
    //链接着色程序对象
    gl.linkProgram(program);
    //判断是否成功
    const success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if(success){
        return program;
    }
    console.log(gl.getProgramInfoLog(program));
    gl.deleteProgram(program);

}


function main(){
    //WebGL大多数元素都是可以通过状态来描述
    //状态是通过内置全局变量gl来调用切换
    //gl指向的是绘图上下文
    //绘图上下文需要通过canvas元素来获取
    //so,第一步,创建canvas元素
    const canvas = document.createElement('canvas');
    //这里解释一下为什么不直接在html中创建canvas元素,而是在js中动态创建
    //因为很多代码是在js中编写的,在这里定义的canvas当前脚本可以判断其类型,而这些属性在html中定义的canvas元素在js中获取是类型是any,无法使用其api代码提示

    //将创建的cānvas元素添加到body中
    document.getElementsByTagName('body')[0].appendChild(canvas)
    //定义画图尺寸
    canvas.width = 400;
    canvas.height = 300;
    //第二步,获取绘图上下文
    const gl = canvas.getContext("webgl");
    //判断当前浏览器是否支持webgl
    if(!gl){
        console.log('不支持webgl');
        return;
    }
    //第三步,编辑顶点着色器
    //定义着色器文本,因为在js中是无法直接使用GLSL语言,但是gl支持将文本转译成着色器,所以需要通过字符串的形式来编写
    const vertexSource = `
    attribute vec2 a_position;//接收一个顶点位置输入
    void main(){
        gl_Position = vec4(a_position,0.0,1.0);//gl_Position是一个Vec4,传入的顶点是一个vec2,所以需要转换成vec4,x,y取之a_position,没有深度所以z为0.0,w设置为1.0
    }
    `
    //创建顶点着色器
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
    //定义顶点数据
    const positions = [
        0,0,
        0,0.5,
        0.7,0
    ]
    //创建缓冲区对象(顶点缓冲对象存储顶点数据)
    const vertexBuffer = gl.createBuffer();
    //在顶点着色器节点会在GPU上创建内存存储顶点数据,顶点缓冲对象就是为了管理这块内存
    //它会在GPU内存储大量的顶点,使用缓冲对象的好处就是可以一次性将大量顶点数据传输到GPU上,而不是每个顶点发送一次
    //顶点缓冲对象的类型是gl.ARRAY_BUFFER,WebGL允许同时绑定多个缓冲,只要类型不同,但同类型的Buffer会替换之前的Buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    //将顶点数据传输到缓冲区对象中
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    //bufferData主要是初始化buffer对象的数据存储,第一个参数是缓冲区类型,第二个参数是设定数据存储区的大小(GPU内存通常不会很大,所以要合理分配内存),第三个参数是数据的使用方式
    //gl.STATIC_DRAW表示数据不会改变,这里因为传入的是一个固定的顶点数据,所以选择gl.STATIC_DRAW
    //gl.DYNAMIC_DRAW表示数据会频繁更新,常用于动态更新
    //gl.STREAM_DRAW表示数据每次渲染都会更新

    //第四步,创建片段着色器
    //定义着色器文本
    const fragmentSource = `
    precision mediump float;
    void main(){
        gl_FragColor = vec4(1.0,0.0,0.5,1.0);//玫红色
    }
    `;
    //创建片段着色器对象
    const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);

    //第五步,创建着色程序
    const program = createProgram(gl, vertexShader, fragmentShader);

    //第六步,设置视口,方便将NDC坐标映射到画布上
    gl.viewport(0, 0, gl.canvas.width,gl.canvas.height);
    //清除画布颜色和颜色缓冲
    gl.clearColor(0, 0, 0, 1);//这里设置成黑色
    //清除画布颜色和颜色缓冲,由于最终呈现在屏幕上的颜色都要从颜色缓冲中读取,所以每次渲染都需要清除颜色缓冲,否则之前的渲染结果会残留导致花屏
    gl.clear(gl.COLOR_BUFFER_BIT);
    //使用着色程序对象
    gl.useProgram(program);
    //获取顶点位置属性变量
    const positonAttributeLocation = gl.getAttribLocation(program, 'a_position');
    //启用顶点位置属性变量
    gl.enableVertexAttribArray(positonAttributeLocation);

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    //将缓冲区对象绑定到顶点位置属性变量上,并设置如何读取数据
    gl.vertexAttribPointer(positonAttributeLocation, 2, gl.FLOAT, false, 0, 0);

    //绘制三角形
    gl.drawArrays(gl.TRIANGLES, 0, 3);
}

main();

保存后直接打开html可以运行调试,源代码也可以断点调试。
image

posted @ 2025-02-19 17:55  EricShx  阅读(46)  评论(0)    收藏  举报