WebGL sa TypeScript

WebGL sa TypeScript

Ipinaliwanag ng artikulong ito ang WebGL sa TypeScript.

Ipapakilala namin ang konsepto ng WebGL, ang pinaka-simpleng configuration, rendering, mga extension, at class design nang sunod-sunod gamit ang sample code.

YouTube Video

WebGL sa TypeScript

Ang WebGL ay isang low-level na API na nagbibigay-daan upang direktang makontrol ang GPU sa loob ng browser.

Sa pamamagitan ng paggamit ng TypeScript, maaari mong lubos na mabawasan ang mga 'pagkakamali sa implementasyon' sa WebGL gamit ang kaligtasan ng uri, pagkumpleto ng code, at estrukturadong pag-code.

Ang TypeScript ay epektibo lalo na sa mga sumusunod na aspeto:.

  • Nagiging malinaw ang mga uri ng mga WebGL na object.
  • Nababawasan ang mga pagkakamali sa paghawak ng mga variable ng shader.
  • Mas madaling matunton ang kabuuang estruktura.

Pinakamababang configuration ng WebGL

Ito ang mga pangunahing kinakailangan upang makapag-render gamit ang WebGL:.

  • Elemento ng <canvas>
  • WebGL na konteksto
  • Vertex shader
  • Fragment shader

Una, gumawa tayo ng estado kung saan maaaring ma-initialize ang screen.

Pagkuha ng Canvas at WebGL Context

Una, kunin ang canvas at ang WebGLRenderingContext.

Dito, gumagamit tayo ng TypeScript na may pagbibigay-diin sa null safety.

 1const canvas = document.getElementById('gl') as HTMLCanvasElement | null;
 2
 3if (!canvas) {
 4	throw new Error('Canvas not found');
 5}
 6
 7const gl = canvas.getContext('webgl');
 8
 9if (!gl) {
10	throw new Error('WebGL not supported');
11}
  • Sa puntong ito, ang gl ang nagsisilbing entry point para sa lahat ng WebGL na operasyon.

Paglilinis ng screen para kumpirmahing gumagana ang WebGL

Bago mag-render, tingnan kung mapupunan ang kulay ng background.

Ito ang unang tsek upang matiyak na tama ang pag-andar ng WebGL.

1gl.clearColor(0.1, 0.1, 0.1, 1.0);
2gl.clear(gl.COLOR_BUFFER_BIT);
  • Hanggang sa puntong ito, mapupuno ang canvas ng madilim na abong kulay.

Ano ang shader?

Sa WebGL, ang pag-drawing ay nilalarawan gamit ang espesyal na wika na tinatawag na GLSL. Sa GLSL, pangunahing naghahanda ka ng dalawang uri ng shaders: vertex shaders at fragment shaders.

Unang gagawa tayo ng 'pinakababang gumaganang shader' gamit ang dalawang shader na ito.

Pag-susulat ng vertex shader

Ang vertex shaders ang nagtatakda kung saan ilalagay ang mga punto o hugis na iguguhit.

Ang sumunod ay simpleng code na naglalagay ng isang punto sa gitna ng screen.

1const vertexShaderSource = `
2attribute vec2 a_position;
3
4void main() {
5	gl_Position = vec4(a_position, 0.0, 1.0);
6}
7`;
  • Ang a_position ay ang coordinates na ipinasa mula sa panig ng JavaScript.

Pag-susulat ng fragment shader

Ang fragment shaders ang nagtatakda ng mga kulay na lilitaw sa screen.

Ngayon, palaging pulang kulay ang output.

1const fragmentShaderSource = `
2precision mediump float;
3
4void main() {
5	gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
6}
7`;
  • Ang precision ay laging kailangang tukuyin sa WebGL.

Paglikha ng function para i-compile ang mga shader

Dahil pare-pareho lang ang proseso ng paggawa ng shader, ginagawa natin itong function.

 1function createShader(
 2	gl: WebGLRenderingContext,
 3	type: number,
 4	source: string
 5): WebGLShader {
 6	const shader = gl.createShader(type);
 7	if (!shader) {
 8		throw new Error('Failed to create shader');
 9	}
10
11	gl.shaderSource(shader, source);
12	gl.compileShader(shader);
13
14	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
15		throw new Error(gl.getShaderInfoLog(shader) ?? 'Shader compile error');
16	}
17
18	return shader;
19}
  • Ang function na ito ay lumilikha ng shader ayon sa uri, i-ccompile ang source code, magtatapon ng error kung pumalpak, at ibabalik ang na-compileng shader kapag matagumpay.

Paglikha ng programa (isang group ng mga shader)

Pagsamahin ang vertex at fragment shaders sa isa.

Ito ay tinatawag na WebGLProgram.

 1const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
 2const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
 3
 4const program = gl.createProgram();
 5if (!program) {
 6	throw new Error('Failed to create program');
 7}
 8
 9gl.attachShader(program, vertexShader);
10gl.attachShader(program, fragmentShader);
11gl.linkProgram(program);
12
13if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
14	throw new Error(gl.getProgramInfoLog(program) ?? 'Program link error');
15}
  • Ang code na ito ay nag-uugnay ng vertex at fragment shader sa isang program at tinitiyak kung posible itong gamitin para mag-render.

Paghahanda ng vertex data

Dito, maghahanda tayo ng vertex data para gumuhit ng tatluhang hugis (trianggulo).

1// Vertex positions for a triangle in clip space (x, y)
2// Coordinates range from -1.0 to 1.0
3const vertices = new Float32Array([
4	0.0,  0.5,   // Top vertex
5	-0.5, -0.5,  // Bottom-left vertex
6	0.5, -0.5,   // Bottom-right vertex
7]);
  • Ang coordinate system sa WebGL ay mula -1.0 hanggang 1.0.

Paglikha ng buffer at pagpapadala nito sa GPU

Sunod, upang magamit ng GPU ang hanay ng vertex data, inililipat natin ang data gamit ang buffer.

1const buffer = gl.createBuffer();
2if (!buffer) {
3	throw new Error('Failed to create buffer');
4}
5
6gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
7gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  • Lumilikha ang code na ito ng buffer para sa vertex data at ipinapadala ang laman nito sa GPU.

Pag-uugnay ng attribute sa buffer

I-connect ang a_position sa shader sa buffer na ginawa dati.

 1const positionLocation = gl.getAttribLocation(program, 'a_position');
 2
 3gl.enableVertexAttribArray(positionLocation);
 4gl.vertexAttribPointer(
 5	positionLocation,
 6	2,
 7	gl.FLOAT,
 8	false,
 9	0,
10	0
11);

Pagre-render

Panghuli, gamitin ang program para magsagawa ng rendering command.

1gl.useProgram(program);
2gl.drawArrays(gl.TRIANGLES, 0, 3);
  • Kapag pinatakbo mo ang program na ito, lalabas ang isang pulang tatsulok.

Pagdidisenyo ng mga klase gamit ang WebGL

Ang WebGL ay isang imperative na API, kaya't mabilis lumaki o lumobo ang code kung walang estruktura. Sa pamamagitan ng class design, malinaw mong mapaghihiwalay ang initialization, rendering, at resource management.

Dito, gagamitin natin ang bentahe ng TypeScript at unti-unting pagandahin ang disenyo para bigyang-diin ang separation of concerns, reusability, at maintainability.

Direksyon ng Disenyo (Pinakasimple pero Praktikal)

Ang klase ay hinati sa mga sumusunod na tungkulin.

  • Ang GLApp ang may hawak ng pangkalahatang initialization at rendering.
  • Ang ShaderProgram ang namamahala sa mga shader.
  • Ang TriangleMesh ang namamahala sa vertex data.

Una, gumawa tayo ng klase na may kontrol sa lahat.

Entry class para sa isang WebGL application

Ang GLApp ang namamahala sa canvas at WebGL context at siya ring panimula para sa rendering.

 1export class GLApp {
 2	private gl: WebGLRenderingContext;
 3	private shader!: ShaderProgram;
 4	private mesh!: TriangleMesh;
 5
 6	constructor(private canvas: HTMLCanvasElement) {
 7		const gl = canvas.getContext('webgl');
 8		if (!gl) {
 9			throw new Error('WebGL not supported');
10		}
11		this.gl = gl;
12	}
13
14	initialize() {
15		this.gl.clearColor(0.1, 0.1, 0.1, 1.0);
16
17		this.shader = new ShaderProgram(this.gl);
18		this.mesh = new TriangleMesh(this.gl);
19	}
20
21	render() {
22		this.gl.clear(this.gl.COLOR_BUFFER_BIT);
23
24		this.shader.use();
25		this.mesh.draw(this.shader);
26	}
27}
  • Ang klaseng ito ang namamahala sa proseso ng initialization at rendering ng WebGL at siya ring may pananagutan sa pag-drawing sa screen gamit ang shaders at mesh.
  • Sa paghiwalay ng initialize at render, mas madali na i-support ang re-initialization at animation sa hinaharap.

Klase para sa pamamahala ng shaders

Pagkatapos, gumawa ng klase para pamahalaan ang shaders, pagsasama-sama ng paggawa, pag-link, at paggamit ng shader sa isang lugar. Ito ay nagbibigay-daan para ang rendering ay tumutok lang sa paggamit sa kanila.

 1export class ShaderProgram {
 2	private program: WebGLProgram;
 3
 4	constructor(private gl: WebGLRenderingContext) {
 5		const vertexSource = `
 6		attribute vec2 a_position;
 7		void main() {
 8			gl_Position = vec4(a_position, 0.0, 1.0);
 9		}
10		`;
11
12		const fragmentSource = `
13		precision mediump float;
14		void main() {
15			gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
16		}
17		`;
18
19		const vs = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
20		const fs = this.createShader(this.gl.FRAGMENT_SHADER, fragmentSource);
21
22		const program = this.gl.createProgram();
23		if (!program) throw new Error('Program create failed');
24
25		this.gl.attachShader(program, vs);
26		this.gl.attachShader(program, fs);
27		this.gl.linkProgram(program);
28
29		if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
30			throw new Error(this.gl.getProgramInfoLog(program) ?? 'Link error');
31		}
32
33		this.program = program;
34	}
35
36	use() {
37		this.gl.useProgram(this.program);
38	}
39
40	getAttribLocation(name: string): number {
41		return this.gl.getAttribLocation(this.program, name);
42	}
43
44	private createShader(type: number, source: string): WebGLShader {
45		const shader = this.gl.createShader(type);
46		if (!shader) throw new Error('Shader create failed');
47
48		this.gl.shaderSource(shader, source);
49		this.gl.compileShader(shader);
50
51		if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
52			throw new Error(this.gl.getShaderInfoLog(shader) ?? 'Compile error');
53		}
54
55		return shader;
56	}
57}
  • Pinag-iisa ng klase na ito ang paggawa, pag-link, at paggamit ng vertex at fragment shaders, kaya ligtas na magagamit ng rendering side ang mga shader.
  • Sa pamamagitan ng paglalantad ng getAttribLocation, maaari itong ligtas na i-reference mula sa mesh side.

Klase para sa pamamahala ng meshes (vertex data)

Gagawa rin tayo ng klase para pamahalaan ang vertex data. Kapag hinati ang mga mesh class ayon sa bawat object na iguguhit, mas madali itong palawakin.

 1export class TriangleMesh {
 2	private buffer: WebGLBuffer;
 3	private vertexCount = 3;
 4
 5	constructor(private gl: WebGLRenderingContext) {
 6		const vertices = new Float32Array([
 7			0.0,  0.5,
 8			-0.5, -0.5,
 9			0.5, -0.5,
10		]);
11
12		const buffer = gl.createBuffer();
13		if (!buffer) throw new Error('Buffer create failed');
14
15		this.buffer = buffer;
16
17		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
18		gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
19	}
20
21	draw(shader: ShaderProgram) {
22		const gl = this.gl;
23		const positionLoc = shader.getAttribLocation('a_position');
24
25		gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
26		gl.enableVertexAttribArray(positionLoc);
27		gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
28
29		gl.drawArrays(gl.TRIANGLES, 0, this.vertexCount);
30	}
31}
  • Ang klase na ito ang namamahala sa vertex data ng triangle at sa pag-uugnay nito sa shader, ito ang tumutukoy sa aktwal na rendering.
  • Lahat ng impormasyong kailangan para mag-render ay nasa loob ng TriangleMesh.

Entry point (Halimbawa ng paggamit)

Sa huli, pagsamahin ang mga klase para simulan ang app.

1const canvas = document.getElementById('gl') as HTMLCanvasElement;
2
3const app = new GLApp(canvas);
4app.initialize();
5app.render();
  • Dahil sa estrukturang ito, madali nang magdagdag ng animation o maraming mesh.

Praktikal na mga payo sa pagsusulat ng WebGL sa TypeScript

Ang paggamit ng TypeScript sa WebGL ay may mga sumusunod na benepisyo:.

  • Sa pagconvert ng mga proseso ng WebGL sa mga klase, naaayos ito ayon sa tungkulin, kaya mas madali ang maintenance at pagdagdag ng feature.
  • Sa paghihiwalay ng responsibilities gaya ng rendering at shader management, bumubuti ang pagiging nababasa ng code.
  • Sa paggamit ng type completion ng TypeScript, napapaliit ang pagkakamali sa pagtawag ng WebGL API o pagpapasa ng mga parameter.

Buod

Sa paggamit ng TypeScript, kahit ang mga low-level na proseso ng WebGL ay kayang asikasuhin nang maayos at may type safety. Sa pag-unawa sa daloy mula sa pinakamababang configuration hanggang rendering, at pag-apply ng class design para maihiwalay ang mga tungkulin tulad ng initialization, rendering, at resource management, mapapabuti mo ang readability at maintainability. Sa pamamagitan ng sunod-sunod na pag-implement, matutunan mo ang WebGL bilang praktikal na kaalaman na hindi isang black box at magagamit sa totoong trabaho.

Maaari mong sundan ang artikulo sa itaas gamit ang Visual Studio Code sa aming YouTube channel. Paki-check din ang aming YouTube channel.

YouTube Video