webgl图形绘制流程

文章目录

  • 前言
  • webgl基本环境搭建
    • webgl上下文
    • gl.clearColor()、gl.clear()
    • canvas填充示例
  • 着色器绘制图形
    • 着色器使用流程
    • 封装初始化着色器代码
  • 开始绘制
    • gl.drawArrays()
    • 绘制点示例
    • gl.viewport()设置窗口大小
    • gl_Position齐次坐标
  • 不足
  • 总结


前言

webgl基本图形绘制上下文,步骤,涉及的方法,含义,实现效果等


webgl基本环境搭建

webgl上下文

实现webgl程序时,首先要创立允许webgl运行的环境,这个环境一般被称为webgl绘图上下文,是基于canvas标签创立的

	// 获取webgl上下文
	let canvas = document.getElementById('canvas');
	const gl = canvas.getContext('webgl');

gl.clearColor()、gl.clear()

gl.clearColor(r,g,b,a):设置背景色

  • r: 红色分量[0.0, 1.0]
  • g: 绿色分量[0.0, 1.0]
  • b: 蓝色分量[0.0, 1.0]
  • a: 透明度[0.0, 1.0]

gl.clearColor(r,g,b,a)中的参数取值范围都在0.0~1.0之间,若超出的话不会报错,会自动设置为边界值。背景色一旦设置,除非再次调用该方法,否则不会改变


gl.clear(buffer):将指定的缓冲区设置为预设的值

  • buffer:所要清空的缓存类型,枚举如下
    – gl.COLOR_BUFFER_BIT: 将颜色缓存清空,采用之前定义的背景色(gl.clearColor())填充
    – gl.DESPTH_BUFFER_BIT: 清除深度缓存,深度用来表示物体在三维世界的前后顺序,体现在遮挡效果上
    – gl.STENCIL_BUFFER_BIT: 模版缓存

canvas填充示例

综上,用一个最简单的示例总结以上方法,屏幕上将会出现由webgl渲染的,被黄色填充的区域

DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>webgltitle>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<style>
		body {
			margin: 0;
			padding: 0;
		}
		canvas {
			width: 500px;
			height: 500px;
		}
	style>
head>

<body>
	<canvas id="canvas">canvas>
	<script>
        /** @type {HTMLCanvasElement} */
		// 获取webgl上下文
		let canvas = document.getElementById('canvas');
		const gl = canvas.getContext('webgl');
		if (!gl) {
			throw new Error('WebGL not supported');
		}

        // 设置背景色
        gl.clearColor(1.0, 1.0, 0.0, 1.0)
        // 清空缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT)
	script>
body>
html>

着色器绘制图形

在之前的文章对着色器的使用过程有过比较详细的介绍,这里会更系统介绍着色器使用步骤以及涉及的方法

着色器使用流程

webgl图形绘制流程_第1张图片
其对应代码如下所示:

		// 创建顶点着色器
		let vertexShader = gl.createShader(gl.VERTEX_SHADER)
		// 创建顶点着色器的代码GLSL
		gl.shaderSource(vertexShader, `
			void main() {
				gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
				gl_PointSize = 20.0;
			}
		`)
		// 编译顶点着色器
		gl.compileShader(vertexShader)
		if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
			console.error(gl.getShaderInfoLog(vertexShader));
			throw new Error('could not compile vertexShader');
		}
		// 创建片元着色器
		let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
		// 创建片元着色器的代码GLSL
		gl.shaderSource(fragmentShader, `
			void main(){
				gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
			}
		`)
		// 编译片元着色器
		gl.compileShader(fragmentShader)
		if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
			console.error(gl.getShaderInfoLog(fragmentShader));
			throw new Error('could not compile fragmentShader');
		}
		// 创建程序
		let program = gl.createProgram();
		console.log(program)
		// 程序关联顶点着色器和片元着色器
		gl.attachShader(program, vertexShader);
		gl.attachShader(program, fragmentShader);
		// 链接程序
		gl.linkProgram(program);

		// 使用程序进行渲染
		gl.useProgram(program);
		if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
			console.log(gl.getProgramInfoLog(program));
			throw new Error('could not link shaders');
		}

封装初始化着色器代码

着色器的使用流程固定,可以将初始着色器的代码封装使用,会更加轻便

function initShader(gl, vertex, fragment) {
			// 创建顶点着色器
			let vertexShader = gl.createShader(gl.VERTEX_SHADER)
			// 创建顶点着色器的代码GLSL
			gl.shaderSource(vertexShader, vertex)
			// 编译顶点着色器
			gl.compileShader(vertexShader)
			if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
				console.error(gl.getShaderInfoLog(vertexShader));
				throw new Error('could not compile vertexShader');
			}
			// 创建片元着色器
			let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
			// 创建片元着色器的代码GLSL
			gl.shaderSource(fragmentShader, fragment)
			// 编译片元着色器
			gl.compileShader(fragmentShader)
			if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
				console.error(gl.getShaderInfoLog(fragmentShader));
				throw new Error('could not compile fragmentShader');
			}
			// 创建程序
			let program = gl.createProgram();
			console.log(program)
			// 程序关联顶点着色器和片元着色器
			gl.attachShader(program, vertexShader);
			gl.attachShader(program, fragmentShader);
			// 链接程序
			gl.linkProgram(program);

			// 使用程序进行渲染
			gl.useProgram(program);
			if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
				console.log(gl.getProgramInfoLog(program));
				throw new Error('could not link shaders');
			}
			return program
		}

开始绘制

gl.drawArrays()

gl.drawArrays(mode, first, count): 执行顶点着色器,按照mode模式绘制图形

  • mode:绘制的方式,常见的类型包括以下几种,详细的可以参照这里
    – gl.Points: 绘制点,count>0
    – gl.Lines: 绘制线,count>1
    – gl.TRIANGLES: 绘制三角形,count>2

  • first:从第几个点开始绘制

  • count:使用几个点进行绘制

当函数gl.drawArrays()执行过程中,顶点着色器供执行了count次,每当执行完顶点着色器,片元着色器都会立即出发执行,如此往复。
`

绘制点示例

结合上述示例,我们绘制一个点在canvas中央

		let canvas = document.getElementById('canvas');

		// 获取webgl绘图上下文
		const gl = canvas.getContext('webgl');
		if (!gl) {
			throw new Error('WebGL not supported');
		}

		canvas.width = window.innerWidth;
		canvas.height = window.innerHeight;
		gl.viewport(0, 0, canvas.width, canvas.height)

		// 设置背景色
		gl.clearColor(1.0, 1.0, 0.0, 1.0)
		// 清空缓冲区
		gl.clear(gl.COLOR_BUFFER_BIT)
		
		const vertex =  `
			void main() {
				gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
				gl_PointSize = 20.0;
			}
		`
		const fragment =  `
			void main(){
				gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
			}
		`
		// 使用shader
		initShader(gl, vertex, fragment)

		// 绘制三角形
		gl.drawArrays(gl.POINTS, 0, 1);

示例:

webgl图形绘制流程_第2张图片

gl.viewport()设置窗口大小

当我设置canvas画布未铺满全屏时,使用顶点着色器会出现坐标偏移的情况:
webgl图形绘制流程_第3张图片
在获取webgl上下文时,可先通过gl.viewport()设置窗口大小

canvas.width = window.innerWidth // 500;
canvas.height = window.innerHeight // 500;
gl.viewport(0, 0, canvas.width, canvas.height)

gl_Position齐次坐标

我们在设置顶点着色器位置时,其值为一个四维向量

void main() {
				gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
				gl_PointSize = 20.0;
			}

gl_Position实际上是一个四维向量,对应的是齐次坐标,齐次坐标的前两项对应浏览器的屏幕坐标,其中心位于(0.0,0.0)处,前两项坐标均处于[-1.0,1.0]之间,如下图:
webgl图形绘制流程_第4张图片

不足

虽然采用上述方法实现了绘制点,但是存在一个明显的问题:绘制的坐标全是静态写死在顶点着色器程序中,无法在javascript中灵活传递坐标。为此可以使用attribute和uniform变量灵活传递变量。

总结

webgl基本环境搭建

  • webgl上下文
  • gl.clearColor()、gl.clear()
  • canvas填充示例

着色器绘制图形

  • 着色器使用流程
  • 封装初始化着色器代码

绘制图形

  • gl.drawArrays()
  • 绘制点示例
  • gl.viewport()设置窗口大小gl_Position齐次坐标

不足

绘制的坐标全是静态写死在顶点着色器程序中,无法在javascript中灵活传递坐标。为此可以使用attribute和uniform变量灵活传递变量

你可能感兴趣的:(WebGL,javascript,webgl)