glsl系列文章开篇

640 阅读3分钟

glsl

前言

本文正在参加「金石计划」  glsl作为着色器语言,webgl就是创建不同的着色器。但是glsl只能绘制出点、线与三角形,它是如何完成绘制的呢? 以下为个人理解~

glsl由顶点着色器vertexShader和片源着色器fragmentShader构成。简单说顶点着色器可以代表的是canvas画布中点的位置,两点成线、三点成面。片源着色器可以理解为像素着色器,例如我们在画布上画一个点,点的大小为10.0,可以理解为占用10x10的像素点也就是100个像素点。当三点确立一个区域即可代表区域内的所有像素点,用片源着色器可以操作区域内像素点的着色~

glsl程序包括哪些东西

下面我们来介绍一个最简单的glsl程序需要哪些东西,熟悉threejs的同学可以知道,它实现一个点的代码量极少。但用传统的glsl画一个点可没有那么简单,下面代码对一个简单glsl程序的封装~

glsl类

export class Webgl {
  constructor(gl) {
    this.gl = gl; 
    this.program = null; //程序
  }
  /*
   *@description: 初始化shader
   *@author: yangj
   *@date: 2023-03-28 09:40:38
   *@return:
   */
  initShader(vertexSource, fragmentSource) {
     //创建shader(shader类型和着色器片段)
    let vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
    let fragmentShader = this.createShader(
      this.gl.FRAGMENT_SHADER,
      fragmentSource
    );
    //创建一个glsl程序
    let program = this.createProgram(vertexShader, fragmentShader);
    if (program) {
      this.gl.useProgram(program);
      this.program = program;
    } else {
      return false;
    }
  }
  /*
   *@description:创建shader
   *@author: yangj
   *@date: 2023-03-28 09:22:36
   *@return:
   */
  createShader(type, source) {
    //创建一个空shader加载器
    let shader = this.gl.createShader(type);
    //加载shader资源
    this.gl.shaderSource(shader, source);
    //编译shader资源
    this.gl.compileShader(shader);
    //编译成功与否
    let compiled = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS);
    if (compiled) {
      return shader;
    } else {
      let error = this.gl.getShaderInfoLog(shader);
      this.gl.deleteShader(shader);
      throw "compiled_error:" + error;
    }
  }
  /*
   *@description:创建程序
   *@author: yangj
   *@date: 2023-03-28 09:22:39
   *@return:
   */
  createProgram(vertexShader, fragmentShader) {
    //创建glsl程序
    let program = this.gl.createProgram();
    //创建成功与否
    if (!program) {
      throw "ceateprogram_error";
    }
    //添加shader
    this.gl.attachShader(program, vertexShader);
    this.gl.attachShader(program, fragmentShader);
    //链接指定glsl程序
    this.gl.linkProgram(program);
    //成功与否
    let linked = this.gl.getProgramParameter(program, this.gl.LINK_STATUS);
    if (linked) {
      return program;
    } else {
      let error = this.gl.getProgramInfoLog(program);
      this.gl.deleteProgram(program);
      this.gl.deleteShader(vertexShader);
      this.gl.deleteShader(fragmentShader);
      throw "program_error:" + error;
    }
  }
}

glsl通信方式

glsl总体来说有三种通信方式,为attribute uniform varying。下面对三种通信方式展开说明~

attribute

attribute是js对vertexShader中的一种传参方式,使用方式在下方~

//一个代表顶点位置的shader
let vertexShader = `
    attribute vec2 a_position;
    void main(){
      gl_Position = vec4(a_position,0.0,1.0);
    }
`;
//第一步获取到指定程序中attribute的地址
let a_position = gl.getAttribLocation(webgl.program, "a_position");
//往地址写入数据
1f -> x
2f -> x,y
3f -> x,y,z
4f -> x,y,z,t
gl.vertexAttrib2f(a_position, 0.5, 0.5);

uniform

uniform 可以用来js对vertexShader和fragmentShader两种shader的传参方式,使用方式在下方~

let vertexShader = `
    attribute vec2 a_position;
    uniform float u_size;
    void main(){
      gl_Position = vec4(a_position,0.0,1.0);
      gl_PointSize= u_size;
    }
`;
//uniform(vertexshader,fragmentshader)
//第一步获取到指定程序中uniform的地址
let u_color = gl.getUniformLocation(webgl.program, "u_Color");
//往地址写入数据
1f -> x
2f -> x,y
3f -> x,y,z
4f -> x,y,z,t
gl.uniform3f(u_color, 1, 0, 0);

varying

varying 适用于vertexShader对fragmentShader的传参方式,使用方式在下方~

let vertexShader = `
    varying vec2 v_position;
    void main(){
      v_position = a_position;
    }
`;
let fragmentShader = `
    varying vec2 v_position;
    void main() {
      gl_FragColor = vec4(v_position,1.0);
    }
`;

glsl画一个三角形

如何使用glsl画一个三角形,我们首先需要明白,三角形由三个点组成,glsl中三个点可以囊括一个区域,我们通过三个顶点和区域着色就可以画出一个三角形~

import { Webgl } from "../app.js";

let vertexShader = `
  attribute vec2 a_position;
  void main(){
    gl_Position = vec4(a_position,0.0,1.0);
  }
`;
let fragmentShader = `
    precision mediump float;
    void main() {
     gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
`;
const gl = document.getElementById("webgl").getContext("webgl");
let webgl = new Webgl(gl);
webgl.initShader(vertexShader, fragmentShader);
//创建顶点坐标buffer
let vertices = [-0.5, 0.0, 0.0, 0.5, 0.5, 0.0];
vertices = new Float32Array(vertices);
//创建一个glsl buffer加载器
let buffer = gl.createBuffer();
//绑定buffer
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//往buffer中传递数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
//把buffer赋值给attribute
let a_position = gl.getAttribLocation(webgl.program, "a_position");
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);
//确认赋值
gl.enableVertexAttribArray(a_position);
//画三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

Snipaste_2023-03-30_11-14-38.png

结语

本系列作为glsl入门系列,本人也才接触glsl语言,后续会根据学习进度不断更新。我会开设一个glsl专栏,glsl系列文章会收纳于专栏中~