WebGL dalam TypeScript
Artikel ini menerangkan tentang WebGL dalam TypeScript.
Kami akan memperkenalkan konsep WebGL, konfigurasi minimum, rendering, lanjutan, dan reka bentuk kelas langkah demi langkah dengan contoh kod.
YouTube Video
WebGL dalam TypeScript
WebGL ialah API tahap rendah yang membolehkan anda memanipulasi GPU secara langsung dalam pelayar.
Dengan menggunakan TypeScript, anda dapat mengurangkan 'kesilapan pelaksanaan' dalam WebGL dengan ketara melalui keselamatan jenis, pelengkapan kod, dan pengkodan berstruktur.
TypeScript amat berkesan dalam perkara berikut:.
- Jenis objek WebGL menjadi jelas.
- Kesilapan dalam pengendalian pemboleh ubah shader dapat dikurangkan.
- Ia menjadi lebih mudah untuk menjejaki struktur.
Konfigurasi minimum WebGL
Berikut adalah keperluan minimum untuk rendering dengan WebGL:.
- Elemen
<canvas> - Konteks
WebGL - Shader vertex
- Shader fragmen
Pertama, mari kita cipta keadaan di mana skrin boleh dinyatakan.
Mendapatkan Canvas dan Konteks WebGL
Pertama, dapatkan canvas dan WebGLRenderingContext.
Di sini, kami menggunakan pengekodan TypeScript dengan penekanan pada keselamatan null.
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}- Pada peringkat ini,
glmenjadi titik masuk untuk semua operasi WebGL.
Membersihkan skrin untuk mengesahkan bahawa WebGL berfungsi
Sebelum rendering, periksa sama ada warna latar belakang boleh diisi.
Ini adalah pemeriksaan awal untuk memastikan WebGL berfungsi dengan betul.
1gl.clearColor(0.1, 0.1, 0.1, 1.0);
2gl.clear(gl.COLOR_BUFFER_BIT);- Setakat ini, kanvas akan diisi dengan warna kelabu gelap.
Apa itu shader?
Dalam WebGL, proses melukis digambarkan menggunakan bahasa khas yang dipanggil GLSL. Dalam GLSL, anda terutamanya menyediakan dua jenis shader: vertex shader dan fragment shader.
Pertama, kami akan mencipta 'shader kerja minimum' menggunakan dua shader ini.
Menulis shader vertex
Shader vertex menentukan kedudukan titik atau bentuk yang akan dilukis.
Berikut adalah kod ringkas yang meletakkan satu titik di tengah skrin.
1const vertexShaderSource = `
2attribute vec2 a_position;
3
4void main() {
5 gl_Position = vec4(a_position, 0.0, 1.0);
6}
7`;a_positionialah koordinat yang dihantar dari bahagian JavaScript.
Menulis shader fragmen
Shader fragmen menentukan warna yang muncul di skrin.
Kali ini, ia sentiasa mengeluarkan warna merah.
1const fragmentShaderSource = `
2precision mediump float;
3
4void main() {
5 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
6}
7`;precisionmesti sentiasa ditentukan dalam WebGL.
Mencipta fungsi untuk mengkompilasi shader
Oleh kerana proses penciptaan shader sentiasa sama, kami jadikan ia sebagai satu fungsi.
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}- Fungsi ini mencipta shader mengikut jenis yang ditetapkan, mengkompilasi kod sumber, membaling ralat jika gagal, dan mengembalikan shader yang dikompilasi jika berjaya.
Mencipta program (satu set shader)
Gabungkan shader vertex dan fragmen menjadi satu.
Ini dipanggil 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}- Kod ini menghubungkan shader vertex dan fragmen ke dalam satu program dan memeriksa sama ada ia boleh digunakan untuk rendering.
Menyediakan data vertex
Di sini, kita akan menyediakan data verteks untuk melukis sebuah segitiga.
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]);- Sistem koordinat dalam WebGL adalah dari
-1.0hingga1.0.
Mencipta buffer dan memindahkannya ke GPU
Seterusnya, untuk membolehkan susunan data verteks digunakan oleh GPU, kita memindahkan data menggunakan penimbal.
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);- Kod ini mencipta buffer untuk menyimpan data vertex dan memindahkan kandungannya ke GPU.
Mengaitkan atribut dengan buffer
Sambungkan a_position dalam shader dengan buffer yang telah dibuat sebelum ini.
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);Rendering
Akhir sekali, gunakan program untuk mengeluarkan arahan rendering.
1gl.useProgram(program);
2gl.drawArrays(gl.TRIANGLES, 0, 3);- Apabila anda menjalankan program ini, segitiga merah akan dipaparkan.
Reka bentuk kelas dengan WebGL
WebGL ialah API imperatif, jadi kod boleh cepat menjadi berlebihan jika ditulis begitu sahaja. Melalui reka bentuk kelas, anda boleh memisahkan dengan jelas inisialisasi, rendering, dan pengurusan sumber.
Di sini, kami akan memanfaatkan kelebihan TypeScript dan secara berperingkat membangunkan reka bentuk untuk menumpukan pada pemisahan tanggungjawab, kebolehgunaan semula, dan kebolehselenggaraan.
Dasar Reka Bentuk (Minimum tetapi Praktikal)
Kelas ini dibahagikan kepada peranan berikut.
GLAppmengawal inisialisasi dan rendering keseluruhan.ShaderProgrammenguruskan shader.TriangleMeshmenguruskan data vertex.
Pertama, mari kita cipta kelas yang mengawal segalanya.
Kelas kemasukan untuk aplikasi WebGL
GLApp menguruskan canvas dan konteks WebGL dan bertindak sebagai titik permulaan untuk 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}- Kelas ini menguruskan proses inisialisasi dan rendering WebGL dan bertanggungjawab melukis ke skrin menggunakan shader dan mesh.
- Dengan memisahkan
initializedanrender, ia lebih mudah untuk menyokong inisialisasi semula dan animasi pada masa akan datang.
Kelas untuk mengurus shader
Seterusnya, cipta kelas untuk mengurus shader, menggabungkan penciptaan, pautan, dan penggunaan shader di satu tempat. Ini membolehkan pihak rendering memberi tumpuan hanya kepada 'penggunaan' shader.
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}- Kelas ini menyatukan penciptaan, penghubungan, dan penggunaan shader vertex dan fragmen, membolehkan rendering menggunakannya dengan selamat.
- Dengan menonjolkan
getAttribLocation, ia boleh dirujuk dengan selamat dari bahagian mesh.
Kelas untuk mengurus mesh (data vertex)
Kita juga akan mencipta kelas untuk mengurus data verteks. Dengan membahagikan kelas mesh mengikut objek untuk dilukis, ia menjadi mudah untuk diperluas.
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}- Kelas ini menguruskan data vertex segitiga dan, dengan menghubungkannya ke shader, mengendalikan rendering sebenar.
- Semua maklumat yang diperlukan untuk rendering terkandung dalam
TriangleMesh.
Titik permulaan (Contoh penggunaan)
Akhir sekali, gabungkan kelas-kelas untuk melancarkan aplikasi.
1const canvas = document.getElementById('gl') as HTMLCanvasElement;
2
3const app = new GLApp(canvas);
4app.initialize();
5app.render();- Dengan struktur ini, menambah animasi atau beberapa mesh menjadi mudah.
Petua praktikal untuk menulis WebGL dalam TypeScript
Menggunakan TypeScript dengan WebGL menawarkan kelebihan berikut:.
- Dengan menukar prosedur WebGL kepada kelas, ia boleh disusun mengikut peranan, memudahkan penyelenggaraan dan pengembangan.
- Dengan memisahkan tanggungjawab seperti rendering dan pengurusan shader, kebolehbacaan kod menjadi lebih baik.
- Dengan memanfaatkan pelengkapan jenis dalam TypeScript, anda boleh mengurangkan kesilapan apabila memanggil API WebGL atau menetapkan parameter.
Ringkasan
Dengan menggunakan TypeScript, walaupun proses WebGL tahap rendah boleh dikendalikan dengan stabil melalui keselamatan jenis dan penstrukturan. Dengan memahami aliran dari konfigurasi minimum ke rendering, dan dengan menggunakan reka bentuk kelas untuk memisahkan peranan seperti inisialisasi, rendering, dan pengurusan sumber, anda boleh meningkatkan kebolehbacaan dan kebolehselenggaraan. Dengan melaksanakan langkah demi langkah, anda boleh mempelajari WebGL sebagai pengetahuan praktikal yang bukan kotak hitam dan boleh digunakan dalam kerja sebenar.
Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.