【愚公系列】2023年12月 WEBGL专题-着色器
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2023年华为云十佳博主,2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀一、什么是着色器
着色器(Shader)是一种编程语言,用于描述如何对场景中的对象进行渲染。它们被用于计算每个像素的颜色、亮度、反射、阴影、透明度等可视化效果。着色器可以被应用于3D模型、2D图形和视频动画中,以创建各种不同的效果和动画。着色器可以被绑定到渲染管道中的不同阶段,如顶点着色器、像素着色器、几何着色器等,以产生各种不同的效果。它们通常使用专门的编程语言,如OpenGL Shading Language(GLSL)或高级着色语言(HLSL)。
简而言之,着色器就是让开发者自己去编写一段程序,用来代替固定渲染管线,来处理图像的渲染。
🔎1.顶点着色器
顶点着色器是一种着色器类型,它是用于3D图形渲染管线中的第一个阶段。它是一个自定义程序,它以每个顶点为输入,并且计算每个顶点的最终屏幕位置。它还对每个顶点执行各种变换和变形,例如旋转、缩放和平移。它还可以对每个顶点进行颜色、纹理和材质属性的计算。顶点着色器通常被用于创建各种特殊效果,如动态几何变换、实时阴影和变形动画。它通常使用着色语言编写,如OpenGL Shading Language(GLSL)或高级着色语言(HLSL)。
🔎2.片元着色器
片元着色器(也称为像素着色器)是在渲染管线中的一个阶段,用于在像素级别上对三维图形进行着色和渲染。它的作用是确定在屏幕上显示的每个像素的颜色和位置,并且可以实现各种效果,如光照、纹理、阴影等。片元着色器会对每个像素进行处理并将其颜色显示在屏幕上,因此它对于图形的最终呈现效果至关重要。
🔎3.着色器工作流程
WebGL着色器工作流程如下:
-
准备数据:WebGL通过缓冲区对象(Buffer Object)来存储着色器需要处理的数据,包括顶点坐标、纹理坐标、法线向量等,这些数据需通过JavaScript传入程序。
-
编写顶点着色器:顶点着色器负责处理顶点数据,将顶点坐标和其他数据转换为可渲染的形式,并传递给片元着色器,每个顶点被处理一次。
-
编写片元着色器:片元着色器负责处理像素的颜色值,将纹理贴图、光照和材质属性等应用到像素上,每个像素被处理一次。
-
编译着色器程序:将顶点着色器和片元着色器编译成着色器程序,并将它们链接起来。
-
传递数据:将准备好的数据传递给着色器程序。
-
渲染图形:使用着色器程序渲染图形,WebGL根据顶点着色器和片元着色器的指令来处理数据,并输出渲染结果。
以上就是WebGL着色器的工作流程,它可以帮助开发者实现各种复杂的3D图形效果。
🔎4.案例
🦋4.1 静态版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>绘制一个点</title>
<!-- //顶点着色器 shader vertex-->
<script type='x-shader/x-vertex' id='shader-vs'>
void main(){
gl_Position=vec4(0.5,0.0,1.0,1.0);
gl_PointSize=20.0;
}
</script>
<!-- // 范围: 0 - 1 -->
<!-- 片元着色器 -->
<script type='x-shader/x-fragment' id='shader-fs'>
void main(){
gl_FragColor=vec4(0.5,0.0,0.0,1.0);
}
</script>
</head>
<body>
<!-- style="width:400px;height:400px" 不要这样-->
<canvas id="webgl" width="400" height="400"></canvas>
<script>
//1. 获取webgl
var canvas = document.getElementById('webgl');
var gl = canvas.getContext('webgl');
gl.clearColor(0.5, 0.5, 0.5, 1.0); //rgba - 0 - 0 1 - 255
gl.clear(gl.COLOR_BUFFER_BIT);
//1. 初始化着色器 ,方式一
var vs_source = document.getElementById('shader-vs').innerHTML,
fs_source = document.getElementById('shader-fs').innerHTML;
//方式二:
// var vs_source = `
// void main(){
// gl_Position=vec4(0.5,0.0,1.0,1.0);
// gl_PointSize=80.0;
// }
// `;
// var fs_source = `
// void main(){
// gl_FragColor=vec4(0.0,0.0,0.0,1.0);
// }
// `;
//2 创建顶点着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
//绑定资源
gl.shaderSource(vertexShader, vs_source);
//编译顶点着色器
gl.compileShader(vertexShader);
//. 2 创建片段着色器
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 绑定资源
gl.shaderSource(fragmentShader, fs_source);
// 编译片段着色器
gl.compileShader(fragmentShader);
// 3.创建一个着色器程序
var glProgram = gl.createProgram();
gl.attachShader(glProgram, vertexShader); //把顶点着色器添加到着色器程序
gl.attachShader(glProgram, fragmentShader); //把片元着色器添加到着色器程序
// 4.把着色器程序链接成一个完整的程序
gl.linkProgram(glProgram);
gl.useProgram(glProgram);
gl.drawArrays(gl.POINTS, 0, 1);
</script>
</body>
</html>
🦋4.2 动态版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>绘制一个点</title>
</head>
<body>
<!-- style="width:400px;height:400px" 不要这样-->
<canvas id="webgl" width="400" height="400"></canvas>
<script>
//1. 获取webgl
var canvas = document.getElementById('webgl');
var gl = canvas.getContext('webgl');
// gl.clearColor(0.5,0.5,0.5,1.0); //rgba - 0 - 0 1 - 255
// gl.clear(gl.COLOR_BUFFER_BIT);
//1. 初始化着色器 ,方式一
//方式二:
//顶点
var vs_source = `
attribute vec4 a_Position;
attribute float a_PointSize;
void main(){
gl_Position=a_Position;
gl_PointSize=a_PointSize;
}
`;
// vec4(0.5,0.0,1.0,1.0)
//片元
var fs_source = `
void main(){
gl_FragColor=vec4(0.5,0.0,0.0,1.0);
}
`;
//2 创建顶点着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
//绑定资源
gl.shaderSource(vertexShader, vs_source);
//编译顶点着色器
gl.compileShader(vertexShader);
//. 2 创建片段着色器
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 绑定资源
gl.shaderSource(fragmentShader, fs_source);
// 编译片段着色器
gl.compileShader(fragmentShader);
// 3.创建一个着色器程序
var glProgram = gl.createProgram();
gl.attachShader(glProgram, vertexShader); //把顶点着色器添加到着色器程序
gl.attachShader(glProgram, fragmentShader); //把片元着色器添加到着色器程序
// 4.把着色器程序链接成一个完整的程序
gl.linkProgram(glProgram);
gl.useProgram(glProgram);
// 获取attribute变量的存储位置 , 返回对应的地址信息
var aPosition = gl.getAttribLocation(glProgram, 'a_Position');
//给变量赋值
gl.vertexAttrib3f(aPosition, 0.0, 0.5, 0.0); //位置
// vertexAttrib函数功能,3:对应需要传3个参数,或者是几维向量,
// f:表示参数是float类型,
var aPointSize = gl.getAttribLocation(glProgram, 'a_PointSize');
gl.vertexAttrib1f(aPointSize, 50.0); //大小
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制
gl.drawArrays(gl.POINTS, 0, 1);
</script>
</body>
</html>
🦋4.3 最终版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 着色器
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 必须要存在 main 函数
void main() {
// 要绘制的点的坐标
gl_Position = vec4(0.0,0.0,0.0,1.0);
// 点的大小
gl_PointSize = 30.0;
}
`; // 顶点着色器
// gl_Position vec4(0.0,0.0,0.0,1.0) x, y, z, w齐次坐标 (x/w, y/w, z/w)
// gl_FragColor vec4(1.0,0.0,0.0,1.0) r, g, b, a
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`; // 片元着色器
// 创建着色器
// const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//
// gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
// gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码
//
// // 编译着色器
// gl.compileShader(vertexShader)
// gl.compileShader(fragmentShader)
//
// // 创建一个程序对象
// const program = gl.createProgram();
//
// gl.attachShader(program, vertexShader)
// gl.attachShader(program, fragmentShader)
//
// gl.linkProgram(program)
//
// gl.useProgram(program)
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
// 执行绘制
// 要绘制的图形是什么, 从哪个开始, 使用几个顶点
gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays(gl.LINES, 0, 1); // 最少需要有两个点,
gl.drawArrays(gl.TRIANGLES, 0, 1); // 3个点
// 3个顶点
// 0.0, 0.0, 0.0
// 0.2, 0.0, 0.0
// 0.4, 0.0, 0.0
gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays(gl.LINES, 1, 2);
</script>
initShader函数的封装
function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
return program;
}
🚀备注
vec4表示四维向量。它由四个分量组成:x, y, z 和 w。w 的意义取决于 vec4 被用于何种目的:
- 用于表示点的四维坐标时,w = 1. 这时 vec4 表示三维空间中的一个点加上一个额外的维度。
- 用于表示方向的四维向量时,w = 0. 这时 vec4 表示一个三维方向加上一个零的第四维分量。
- 用于表示四元数时,w 为实部,x、y、z 为虚部。四元数可以用来表示旋转和方向。
- 用于表示仿射变换(例如平移、缩放、旋转)的矩阵时,w 用于指定平移分量。
🚀感谢:给读者的一封信
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。
如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。
再次感谢您的阅读和支持!
最诚挚的问候, “愚公搬代码”
- 点赞
- 收藏
- 关注作者
评论(0)