本文用于创建一个用在 WebGL 上的 GLSL 的快速预览环境,便于后续学习 GLSL 着色器语言。
VS code
首先是 vscode 的本地编写,这里需要两个扩展:
- 用于 Shader 语言的支持:Shader languages support for VS Code - Visual Studio Marketplace
- 用于快速预览 Shader 的 preview 热更新插件:glsl-canvas - Visual Studio Marketplace
注意第二个插件的使用方式仅支持 vsc 的命令调用,没有按钮可以触发。因此若 vsc 安装在 C 盘且没有管理员权限时,可能会报错:show glsl-canvas 命令错误,可能是写入命令菜单时写权限不够导致,此时建议去 vscode market 下载插件包离线安装。
到此处 vscode 的准备完成,可以创建.vert
和.frag
后缀文件编写顶点着色器(vertexShader)和片元着色器(fragmentShader),片元着色器可以写完后立即用 glsl-canvas 插件预览,不需要顶点着色器,插件会自动提供几个进行渲染。
WebGL demo
此处使用 Vue 框架,但不使用也可以,该示例与框架无关,只要创建一个 canvas 元素并拿到 DOM 执行脚本就可以了。
index.vue
<template>
<canvas id="renderCanvas" width="1000" height="1000"></canvas>
</template>
<style scoped>
#renderCanvas {
display: block;
margin: auto;
width: min(90vh,90vw);
height: min(90vh,90vw);
}
</style>
<script>
import main from "./script";
export default {
mounted() {
main();
},
};
</script>
script.js
const vertexShader = `
void main(){
gl_Position=vec4(0.0,0.0,0.0,1.0);
gl_PointSize=1000.0;
}`;
const fragmentShader = `
#ifdef GL_ES
precision mediump float;
#endif
void main(){
vec2 st = gl_FragCoord.xy / vec2(800,800) - 0.5;
float lengthV = length(st);
float stepV = step(0.3,lengthV);
gl_FragColor = vec4(stepV,stepV,0,1.0);
}
`;
export default function main() {
// 获取webgl的上下文
var canvas = document.getElementById("renderCanvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// 设置清空颜色
gl.clearColor(1, 1, 1, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 初始化着色器(定点着色器和片段着色器)
// 顶点着色器:就是定义点的位置、大小
// 片元着色器:定义画出来的物体的材质(颜色、反光度等...)
// 创建顶点着色器对象
var _vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShader);
// 创建片元着色器对象
var _fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShader);
// 给片元着色器对象绑定定义代码
// 创建一个着色器程序
var glProgram = gl.createProgram();
// 把前面创建的二个着色器对象添加到着色器程序中(顶点和片段着色器都需要)
gl.attachShader(glProgram, _vertexShader);
gl.attachShader(glProgram, _fragmentShader);
// 把着色器程序链接成一个完整的程序
gl.linkProgram(glProgram);
// 使用这个完整的程序
gl.useProgram(glProgram);
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source.trim());
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
此时,仅需要修改两个着色器代码就可以进行预览了。
vscode 写进 String Template 的 GLSL 作为字符串可读性不好?可以用下面这两个插件来解决:^1
- Comment tagged templates
- Shader languages support
const vertexshader = /* glsl */ `
void main(){
....
}
`;
注意
引入方式
上述环境为了省事直接将 vertexShader 和 fragmentShader 代码作为字符串使用,效果可以实现但不太利于组织代码。
在 html 文件中支持 script 标签写法,通过document.querySelector("#vertex-shader-2d").text
获取 shader 文本。
<script id="vertex_shader" type="shader/x-vertex">
void main(){
gl_Position=vec4(0.0,0.0,0.0,1.0);
gl_PointSize=1000.0;
}
</script>
<script id="fragment_shader" type="shader/x-vertex">
#ifdef GL_ES
precision mediump float;
#endif
void main(){
vec2 st = gl_FragCoord.xy / vec2(800,800) - 0.5;
float lengthV = length(st);
float stepV = step(0.3,lengthV);
gl_FragColor = vec4(stepV,stepV,0,1.0);
}
</script>
在 webpack 工程中可以通过raw-loader
(功能是将被引入的文件内容转换为字符串)读 glsl 文件并编译。例:
{
test: /\.glsl$/,
loader: 'raw-loader'
}
版本声明
对代码文本使用trim()
方法,shader 的版本声明#version 300 es
必须在第一行,不许空行不许空格,并且要在顶点着色器和片元着色器中同时指定。
var vsSource = ` // bad, a blank line here.
#version 300 es
`;
var vsSource = `#version 300 es // good
`;
<script id="vs" type="notjs"> // bad, a blank line here.
#version 300 es
...
</script>
<script id="vs" type="notjs">#version 300 es // good
...
</script>
参考资源
^1 : 使用 WebGL 和 Shaders 进行高级创意编码 Advanced Creative Coding with WebGL & Shaders