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