├── .gitignore ├── Lesson0_Whole_new_start ├── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── index.html │ │ └── main.ts │ ├── tsconfig.json │ └── vite.config.js └── Tutorial │ └── Lesson0_Whole_new_start.md ├── Lesson1_Triangle_and_square ├── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── index.html │ │ ├── main.ts │ │ └── shader │ │ │ ├── fragment.wgsl.ts │ │ │ └── vertex.wgsl.ts │ ├── tsconfig.json │ └── vite.config.js ├── Tutorial │ ├── Lesson1_Triangle_and_square.md │ └── image │ │ ├── 3ds_max_nitrous_software_renderer.png │ │ ├── edge3.png │ │ ├── gpu_compute.png │ │ ├── lesson1_result.png │ │ ├── nvidia_control_panel.png │ │ ├── nvidia_display_card.jpg │ │ ├── nvidia_wechat.jpg │ │ ├── only_triangle.png │ │ └── windows_hardware_manager.png └── package-lock.json ├── Lesson2_Add_Color ├── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── index.html │ │ ├── main.ts │ │ └── shader │ │ │ ├── fragment.wgsl.ts │ │ │ └── vertex.wgsl.ts │ ├── tsconfig.json │ └── vite.config.js └── Tutorial │ ├── Lesson2_Add_colors.md │ └── image │ ├── gta5_out_of_memory.jpg │ └── lesson2_add_colors.png ├── Lesson3_Animate ├── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── index.html │ │ ├── main.ts │ │ └── shader │ │ │ ├── fragment.wgsl.ts │ │ │ └── vertex.wgsl.ts │ ├── tsconfig.json │ └── vite.config.js └── Tutorial │ ├── Lesson3_Animate.md │ └── image │ └── animate.gif ├── Lesson4_Someting_real_3D └── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── index.html │ ├── main.ts │ └── shader │ │ ├── fragment.wgsl.ts │ │ └── vertex.wgsl.ts │ ├── tsconfig.json │ └── vite.config.js ├── Lesson5_Texture └── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── image.d.ts │ ├── index.html │ ├── main.ts │ ├── shader │ │ ├── fragment.wgsl.ts │ │ └── vertex.wgsl.ts │ └── texture │ │ └── nehe.gif │ ├── tsconfig.json │ └── vite.config.js ├── Lesson6_Interactive_and_texture_filter └── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── image.d.ts │ ├── index.html │ ├── main.ts │ ├── shader │ │ ├── fragment.wgsl.ts │ │ └── vertex.wgsl.ts │ └── texture │ │ └── crate.gif │ ├── tsconfig.json │ └── vite.config.js ├── Lesson7_Directional_light_and_ambient_light └── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── image.d.ts │ ├── index.html │ ├── main.ts │ ├── shader │ │ ├── fragment.glsl.ts │ │ └── vertex.glsl.ts │ └── texture │ │ └── crate.gif │ ├── tsconfig.json │ └── vite.config.js ├── LessonX_RenderBundle └── Code │ ├── .babelrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── index.html │ ├── main.ts │ └── shader │ │ ├── fragment.wgsl.ts │ │ └── vertex.wgsl.ts │ ├── tsconfig.json │ └── vite.config.js ├── README.md └── gpuweb-explainer ├── explainer.md └── figure1.svg /.gitignore: -------------------------------------------------------------------------------- 1 | LessonX_Draw_Indirect 2 | LessonX_OffscreenCanvas 3 | -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"] 3 | } -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson0-whole-new-start", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --host", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | export class App { 2 | 3 | public canvas: HTMLCanvasElement; 4 | 5 | public context: GPUCanvasContext; 6 | 7 | public devicePixelWidth: number; 8 | 9 | public devicePixelHeight: number; 10 | 11 | public CreateCanvas( rootElement: HTMLElement ) { 12 | 13 | let width = rootElement.clientWidth; 14 | 15 | let height = rootElement.clientHeight; 16 | 17 | this.devicePixelWidth = width * window.devicePixelRatio; 18 | 19 | this.devicePixelHeight = height * window.devicePixelRatio; 20 | 21 | this.canvas = document.createElement( 'canvas' ); 22 | 23 | this.canvas.width = this.devicePixelWidth; 24 | 25 | this.canvas.height = this.devicePixelHeight; 26 | 27 | this.canvas.style.width = '100%'; 28 | 29 | this.canvas.style.height = '100%'; 30 | 31 | rootElement.appendChild( this.canvas ); 32 | 33 | } 34 | 35 | public InitWebGPU() { 36 | 37 | this.context = this.canvas.getContext( 'webgpu' ) as GPUCanvasContext; 38 | 39 | if ( this.context ) { 40 | 41 | console.info( `Congratulations! You've got a WebGPU context!` ); 42 | 43 | } else { 44 | 45 | throw new Error( 'Your browser seems not support WebGPU!' ); 46 | 47 | } 48 | 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 0 - Whole new start 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | 3 | let main = () => { 4 | 5 | let app = new App(); 6 | 7 | app.CreateCanvas( document.body ) 8 | 9 | app.InitWebGPU(); 10 | 11 | } 12 | 13 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson0_Whole_new_start/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson1-triangle-and-square", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from 'three'; 2 | 3 | export class App { 4 | 5 | public canvas: HTMLCanvasElement; 6 | 7 | public adapter: GPUAdapter; 8 | 9 | public device: GPUDevice; 10 | 11 | public context: GPUCanvasContext; 12 | 13 | public format: GPUTextureFormat = 'bgra8unorm'; 14 | 15 | public commandEncoder: GPUCommandEncoder; 16 | 17 | public renderPassEncoder: GPURenderPassEncoder; 18 | 19 | public uniformGroupLayout: GPUBindGroupLayout; 20 | 21 | public renderPipeline: GPURenderPipeline; 22 | 23 | public devicePixelWidth: number; 24 | 25 | public devicePixelHeight: number; 26 | 27 | public CreateCanvas (rootElement: HTMLElement) { 28 | 29 | let width = rootElement.clientWidth; 30 | 31 | let height = rootElement.clientHeight; 32 | 33 | this.devicePixelWidth = Math.floor(width * window.devicePixelRatio); 34 | 35 | this.devicePixelHeight = Math.floor(height * window.devicePixelRatio); 36 | 37 | this.canvas = document.createElement('canvas'); 38 | 39 | this.canvas.width = this.devicePixelWidth; 40 | 41 | this.canvas.height = this.devicePixelHeight; 42 | 43 | this.canvas.style.width = '100%'; 44 | 45 | this.canvas.style.height = '100%'; 46 | 47 | rootElement.appendChild(this.canvas); 48 | 49 | } 50 | 51 | public async InitWebGPU () { 52 | 53 | this.adapter = await navigator.gpu.requestAdapter({ 54 | 55 | powerPreference: 'high-performance' 56 | 57 | }); 58 | 59 | this.device = await this.adapter.requestDevice(); 60 | 61 | this.context = this.canvas.getContext('webgpu') as GPUCanvasContext; 62 | 63 | this.format = navigator.gpu.getPreferredCanvasFormat(); 64 | 65 | this.context.configure({ 66 | 67 | device: this.device, 68 | 69 | format: this.format, 70 | 71 | usage: GPUTextureUsage.RENDER_ATTACHMENT 72 | 73 | }); 74 | 75 | } 76 | 77 | public InitRenderPass (clearColor: GPUColorDict) { 78 | 79 | this.commandEncoder = this.device.createCommandEncoder(); 80 | 81 | let renderPassDescriptor: GPURenderPassDescriptor = { 82 | 83 | colorAttachments: [{ 84 | 85 | view: this.context.getCurrentTexture().createView(), 86 | 87 | loadOp: 'clear', 88 | 89 | storeOp: 'store', 90 | 91 | clearValue: clearColor 92 | 93 | }] 94 | 95 | } 96 | 97 | this.renderPassEncoder = this.commandEncoder.beginRenderPass(renderPassDescriptor); 98 | 99 | this.renderPassEncoder.setViewport(0, 0, this.devicePixelWidth, this.devicePixelHeight, 0, 1); 100 | 101 | } 102 | 103 | 104 | public InitPipeline (vxCode: string, fxCode: string) { 105 | 106 | this.uniformGroupLayout = this.device.createBindGroupLayout({ 107 | 108 | entries: [ 109 | 110 | { 111 | 112 | binding: 0, 113 | 114 | visibility: GPUShaderStage.VERTEX, 115 | 116 | buffer: { 117 | 118 | type: 'uniform', 119 | 120 | } 121 | 122 | } 123 | 124 | ] 125 | 126 | }); 127 | 128 | let layout: GPUPipelineLayout = this.device.createPipelineLayout({ 129 | 130 | bindGroupLayouts: [this.uniformGroupLayout] 131 | 132 | }); 133 | 134 | let vxModule: GPUShaderModule = this.device.createShaderModule({ 135 | 136 | code: vxCode 137 | 138 | }); 139 | 140 | let fxModule: GPUShaderModule = this.device.createShaderModule({ 141 | 142 | code: fxCode 143 | 144 | }); 145 | 146 | this.renderPipeline = this.device.createRenderPipeline({ 147 | 148 | layout: layout, 149 | 150 | vertex: { 151 | 152 | buffers: [ 153 | 154 | { 155 | 156 | arrayStride: 4 * 3, 157 | 158 | attributes: [ 159 | 160 | // position 161 | 162 | { 163 | 164 | shaderLocation: 0, 165 | 166 | offset: 0, 167 | 168 | format: 'float32x3' 169 | 170 | } 171 | 172 | ] 173 | 174 | } 175 | 176 | ], 177 | 178 | module: vxModule, 179 | 180 | entryPoint: 'main', 181 | 182 | }, 183 | 184 | fragment: { 185 | 186 | module: fxModule, 187 | 188 | entryPoint: 'main', 189 | 190 | targets: [ 191 | 192 | { 193 | format: this.format, 194 | 195 | } 196 | 197 | ] 198 | 199 | }, 200 | 201 | primitive: { 202 | 203 | topology: 'triangle-list', 204 | 205 | } 206 | 207 | }); 208 | 209 | this.renderPassEncoder.setPipeline(this.renderPipeline); 210 | 211 | } 212 | 213 | private _CreateGPUBuffer (typedArray: TypedArray, usage: GPUBufferUsageFlags) { 214 | 215 | let gpuBuffer = this.device.createBuffer({ 216 | 217 | size: typedArray.byteLength, 218 | 219 | usage: usage | GPUBufferUsage.COPY_DST, 220 | 221 | mappedAtCreation: true 222 | 223 | }); 224 | 225 | let constructor = typedArray.constructor as new (buffer: ArrayBuffer) => TypedArray; 226 | 227 | let view = new constructor(gpuBuffer.getMappedRange()); 228 | 229 | view.set(typedArray, 0); 230 | 231 | gpuBuffer.unmap(); 232 | 233 | return gpuBuffer; 234 | 235 | } 236 | 237 | public InitGPUBuffer (vxArray: Float32Array, idxArray: Uint32Array, mxArray: Float32Array) { 238 | 239 | let vertexBuffer = this._CreateGPUBuffer(vxArray, GPUBufferUsage.VERTEX); 240 | 241 | this.renderPassEncoder.setVertexBuffer(0, vertexBuffer); 242 | 243 | let indexBuffer = this._CreateGPUBuffer(idxArray, GPUBufferUsage.INDEX); 244 | 245 | this.renderPassEncoder.setIndexBuffer(indexBuffer, "uint32"); 246 | 247 | let uniformBuffer = this._CreateGPUBuffer(mxArray, GPUBufferUsage.UNIFORM); 248 | 249 | let uniformBindGroup = this.device.createBindGroup({ 250 | 251 | layout: this.uniformGroupLayout, 252 | 253 | entries: [{ 254 | 255 | binding: 0, 256 | 257 | resource: { buffer: uniformBuffer } 258 | 259 | }] 260 | 261 | }); 262 | 263 | this.renderPassEncoder.setBindGroup(0, uniformBindGroup); 264 | 265 | } 266 | 267 | public Draw (indexCount: number) { 268 | 269 | this.renderPassEncoder.drawIndexed(indexCount, 1, 0, 0, 0); 270 | 271 | } 272 | 273 | public Present () { 274 | 275 | this.renderPassEncoder.end(); 276 | 277 | this.device.queue.submit([this.commandEncoder.finish()]); 278 | 279 | } 280 | 281 | } -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 1 - Triangle and Square 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | 6 | const triangleVertex = new Float32Array( [ 7 | 8 | 0.0, 1.0, 0.0, 9 | -1.0, -1.0, 0.0, 10 | 1.0, -1.0, 0.0 11 | 12 | ]); 13 | 14 | const triangleIndex = new Uint32Array( [ 0, 1, 2 ] ); 15 | 16 | const triangleMVMatrix = new Matrix4().makeTranslation( -1.5, 0.0, -7.0 ); 17 | 18 | const squareVertex = new Float32Array( [ 19 | 20 | 1.0, 1.0, 0.0, 21 | -1.0, 1.0, 0.0, 22 | 1.0, -1.0, 0.0, 23 | -1.0, -1.0, 0.0 24 | 25 | ]); 26 | 27 | const squareIndex = new Uint32Array( [ 0, 1, 2, 1, 2, 3 ] ); 28 | 29 | const squareMVMatrix = new Matrix4().makeTranslation( 1.5, 0.0, -7.0 ); 30 | 31 | let main = async () => { 32 | 33 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 34 | 35 | let pMatrix = camera.projectionMatrix; 36 | 37 | let triangleUniformBufferView = new Float32Array( pMatrix.toArray().concat( triangleMVMatrix.toArray() ) ); 38 | 39 | let squareUniformBufferView = new Float32Array( pMatrix.toArray().concat( squareMVMatrix.toArray() ) ); 40 | 41 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 42 | 43 | let app = new App(); 44 | 45 | app.CreateCanvas( document.body ); 46 | 47 | await app.InitWebGPU(); 48 | 49 | app.InitRenderPass( backgroundColor ); 50 | 51 | app.InitPipeline( vxCode, fxCode ); 52 | 53 | app.InitGPUBuffer( triangleVertex, triangleIndex, triangleUniformBufferView ); 54 | 55 | app.Draw( triangleIndex.length ); 56 | 57 | app.InitGPUBuffer( squareVertex, squareIndex, squareUniformBufferView ); 58 | 59 | app.Draw( squareIndex.length ); 60 | 61 | app.Present(); 62 | 63 | } 64 | 65 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@fragment 3 | fn main() -> @location(0) vec4 { 4 | return vec4(1.0, 1.0, 1.0, 1.0); 5 | }`; -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4, 4 | @size(64) uMVMatrix: mat4x4 5 | }; 6 | 7 | @group(0) @binding(0) 8 | var uniforms: Uniforms; 9 | 10 | @vertex 11 | fn main ( 12 | @location(0) aVertexPosition : vec3 13 | ) -> @builtin(position) vec4 { 14 | return uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 15 | }`; -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/3ds_max_nitrous_software_renderer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/3ds_max_nitrous_software_renderer.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/edge3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/edge3.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/gpu_compute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/gpu_compute.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/lesson1_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/lesson1_result.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/nvidia_control_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/nvidia_control_panel.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/nvidia_display_card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/nvidia_display_card.jpg -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/nvidia_wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/nvidia_wechat.jpg -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/only_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/only_triangle.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/Tutorial/image/windows_hardware_manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson1_Triangle_and_square/Tutorial/image/windows_hardware_manager.png -------------------------------------------------------------------------------- /Lesson1_Triangle_and_square/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson2-add-color", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from 'three'; 2 | 3 | export class App { 4 | 5 | public canvas: HTMLCanvasElement; 6 | 7 | public adapter: GPUAdapter; 8 | 9 | public device: GPUDevice; 10 | 11 | public context: GPUCanvasContext; 12 | 13 | public format: GPUTextureFormat = 'bgra8unorm'; 14 | 15 | public commandEncoder: GPUCommandEncoder; 16 | 17 | public renderPassEncoder: GPURenderPassEncoder; 18 | 19 | public uniformGroupLayout: GPUBindGroupLayout; 20 | 21 | public renderPipeline: GPURenderPipeline 22 | 23 | public devicePixelWidth: number; 24 | 25 | public devicePixelHeight: number; 26 | 27 | public CreateCanvas( rootElement: HTMLElement ) { 28 | 29 | let width = rootElement.clientWidth; 30 | 31 | let height = rootElement.clientHeight; 32 | 33 | this.devicePixelWidth = width * window.devicePixelRatio; 34 | 35 | this.devicePixelHeight = height * window.devicePixelRatio; 36 | 37 | this.canvas = document.createElement( 'canvas' ); 38 | 39 | this.canvas.width = this.devicePixelWidth; 40 | 41 | this.canvas.height = this.devicePixelHeight; 42 | 43 | this.canvas.style.width = '100%'; 44 | 45 | this.canvas.style.height = '100%'; 46 | 47 | rootElement.appendChild( this.canvas ); 48 | 49 | } 50 | 51 | public async InitWebGPU() { 52 | 53 | this.adapter = await navigator.gpu.requestAdapter( { 54 | 55 | powerPreference: 'high-performance' 56 | 57 | } ); 58 | 59 | this.device = await this.adapter.requestDevice(); 60 | 61 | this.context = this.canvas.getContext( 'webgpu' ) as GPUCanvasContext; 62 | 63 | this.format = this.context.getPreferredFormat( this.adapter ); 64 | 65 | this.context.configure( { 66 | 67 | device: this.device, 68 | 69 | format: this.format, 70 | 71 | usage: GPUTextureUsage.RENDER_ATTACHMENT 72 | 73 | } ); 74 | 75 | this.commandEncoder = this.device.createCommandEncoder(); 76 | 77 | } 78 | 79 | public InitRenderPass( clearColor: GPUColorDict ) { 80 | 81 | let renderPassDescriptor: GPURenderPassDescriptor = { 82 | 83 | colorAttachments: [ { 84 | 85 | view: this.context.getCurrentTexture().createView(), 86 | 87 | loadOp: 'clear', 88 | 89 | storeOp: 'store', 90 | 91 | clearValue: clearColor, 92 | 93 | } ] 94 | 95 | } 96 | 97 | this.renderPassEncoder = this.commandEncoder.beginRenderPass( renderPassDescriptor ); 98 | 99 | this.renderPassEncoder.setViewport( 0, 0, this.devicePixelWidth, this.devicePixelHeight, 0, 1 ); 100 | 101 | } 102 | 103 | public InitPipelineWitMultiBuffers( vxCode: string, fxCode: string ) { 104 | 105 | this.uniformGroupLayout = this.device.createBindGroupLayout( { 106 | 107 | entries: [ 108 | 109 | { 110 | 111 | binding: 0, 112 | 113 | visibility: GPUShaderStage.VERTEX, 114 | 115 | buffer: { 116 | 117 | type: 'uniform' 118 | 119 | } 120 | 121 | } 122 | 123 | ] 124 | 125 | } ); 126 | 127 | let layout: GPUPipelineLayout = this.device.createPipelineLayout( { 128 | 129 | bindGroupLayouts: [ this.uniformGroupLayout ] 130 | 131 | } ); 132 | 133 | let vxModule: GPUShaderModule = this.device.createShaderModule( { 134 | 135 | code: vxCode 136 | 137 | } ); 138 | 139 | let fxModule: GPUShaderModule = this.device.createShaderModule( { 140 | 141 | code: fxCode 142 | 143 | } ); 144 | 145 | this.renderPipeline = this.device.createRenderPipeline( { 146 | 147 | layout: layout, 148 | 149 | vertex: { 150 | 151 | buffers: [ 152 | 153 | { 154 | 155 | arrayStride: 4 * 3, 156 | 157 | attributes: [ 158 | 159 | // position 160 | 161 | { 162 | 163 | shaderLocation: 0, 164 | 165 | offset: 0, 166 | 167 | format: 'float32x3' 168 | 169 | } 170 | 171 | ], 172 | 173 | stepMode: 'vertex' 174 | 175 | }, 176 | 177 | { 178 | 179 | arrayStride: 4 * 4, 180 | 181 | attributes: [ 182 | 183 | // color 184 | 185 | { 186 | 187 | shaderLocation: 1, 188 | 189 | offset: 0, 190 | 191 | format: 'float32x4' 192 | 193 | } 194 | 195 | ], 196 | 197 | stepMode: 'instance' 198 | 199 | } 200 | 201 | ], 202 | 203 | module: vxModule, 204 | 205 | entryPoint: 'main' 206 | 207 | }, 208 | 209 | fragment: { 210 | 211 | module: fxModule, 212 | 213 | entryPoint: 'main', 214 | 215 | targets: [ 216 | 217 | { 218 | 219 | format: this.format 220 | 221 | } 222 | 223 | ] 224 | 225 | }, 226 | 227 | primitive: { 228 | 229 | topology: 'triangle-list' 230 | 231 | } 232 | 233 | } ); 234 | 235 | this.renderPassEncoder.setPipeline( this.renderPipeline ); 236 | 237 | } 238 | 239 | public InitPipeline( vxCode: string, fxCode: string ) { 240 | 241 | this.uniformGroupLayout = this.device.createBindGroupLayout( { 242 | 243 | entries: [ 244 | 245 | { 246 | 247 | binding: 0, 248 | 249 | visibility: GPUShaderStage.VERTEX, 250 | 251 | buffer: { 252 | 253 | type: 'uniform' 254 | 255 | } 256 | 257 | } 258 | 259 | ] 260 | 261 | } ); 262 | 263 | let layout: GPUPipelineLayout = this.device.createPipelineLayout( { 264 | 265 | bindGroupLayouts: [ this.uniformGroupLayout ] 266 | 267 | } ); 268 | 269 | let vxModule: GPUShaderModule = this.device.createShaderModule( { 270 | 271 | code: vxCode 272 | 273 | } ); 274 | 275 | let fxModule: GPUShaderModule = this.device.createShaderModule( { 276 | 277 | code: fxCode 278 | 279 | } ); 280 | 281 | this.renderPipeline = this.device.createRenderPipeline( { 282 | 283 | layout: layout, 284 | 285 | vertex: { 286 | 287 | buffers: [ 288 | 289 | { 290 | 291 | arrayStride: 4 * ( 3 + 4 ), 292 | 293 | attributes: [ 294 | 295 | // position 296 | 297 | { 298 | 299 | shaderLocation: 0, 300 | 301 | offset: 0, 302 | 303 | format: 'float32x3' 304 | 305 | }, 306 | 307 | // color 308 | 309 | { 310 | 311 | shaderLocation: 1, 312 | 313 | offset: 4 * 3, 314 | 315 | format: 'float32x4' 316 | 317 | } 318 | 319 | ] 320 | 321 | } 322 | 323 | ], 324 | 325 | module: vxModule, 326 | 327 | entryPoint: 'main' 328 | 329 | }, 330 | 331 | fragment: { 332 | 333 | module: fxModule, 334 | 335 | entryPoint: 'main', 336 | 337 | targets: [ 338 | 339 | { 340 | 341 | format: this.format 342 | 343 | } 344 | 345 | ] 346 | 347 | }, 348 | 349 | primitive: { 350 | 351 | topology: 'triangle-list' 352 | 353 | } 354 | 355 | } ); 356 | 357 | this.renderPassEncoder.setPipeline( this.renderPipeline ); 358 | 359 | } 360 | 361 | private _CreateGPUBuffer( typedArray: TypedArray, usage: GPUBufferUsageFlags ) { 362 | 363 | let gpuBuffer = this.device.createBuffer( { 364 | 365 | size: typedArray.byteLength, 366 | 367 | usage: usage | GPUBufferUsage.COPY_DST, 368 | 369 | mappedAtCreation: true 370 | 371 | } ); 372 | 373 | let constructor = typedArray.constructor as new ( buffer: ArrayBuffer ) => TypedArray; 374 | 375 | let view = new constructor( gpuBuffer.getMappedRange() ); 376 | 377 | view.set( typedArray, 0 ); 378 | 379 | gpuBuffer.unmap(); 380 | 381 | return gpuBuffer; 382 | 383 | } 384 | 385 | public InitGPUBufferWithMultiBuffers( vxArray: Float32Array, colorArray: Float32Array, idxArray: Uint32Array, mxArray: Float32Array ) { 386 | 387 | let vertexBuffer = this._CreateGPUBuffer( vxArray, GPUBufferUsage.VERTEX ); 388 | 389 | this.renderPassEncoder.setVertexBuffer( 0, vertexBuffer ); 390 | 391 | let colorBuffer = this._CreateGPUBuffer( colorArray, GPUBufferUsage.VERTEX ); 392 | 393 | this.renderPassEncoder.setVertexBuffer( 1, colorBuffer, 0 ); 394 | 395 | let indexBuffer = this._CreateGPUBuffer( idxArray, GPUBufferUsage.INDEX ); 396 | 397 | this.renderPassEncoder.setIndexBuffer( indexBuffer, "uint32" ); 398 | 399 | let uniformBuffer = this._CreateGPUBuffer( mxArray, GPUBufferUsage.UNIFORM ); 400 | 401 | let uniformBindGroup = this.device.createBindGroup( { 402 | 403 | layout: this.uniformGroupLayout, 404 | 405 | entries: [ { 406 | 407 | binding: 0, 408 | 409 | resource: { buffer: uniformBuffer } 410 | 411 | } ] 412 | 413 | } ); 414 | 415 | this.renderPassEncoder.setBindGroup( 0, uniformBindGroup ); 416 | 417 | } 418 | 419 | public InitGPUBuffer( vxArray: Float32Array, idxArray: Uint32Array, mxArray: Float32Array ) { 420 | 421 | let vertexBuffer = this._CreateGPUBuffer( vxArray, GPUBufferUsage.VERTEX ); 422 | 423 | this.renderPassEncoder.setVertexBuffer( 0, vertexBuffer ); 424 | 425 | let indexBuffer = this._CreateGPUBuffer( idxArray, GPUBufferUsage.INDEX ); 426 | 427 | this.renderPassEncoder.setIndexBuffer( indexBuffer, "uint32" ); 428 | 429 | let uniformBuffer = this._CreateGPUBuffer( mxArray, GPUBufferUsage.UNIFORM ); 430 | 431 | let uniformBindGroup = this.device.createBindGroup( { 432 | 433 | layout: this.uniformGroupLayout, 434 | 435 | entries: [ { 436 | 437 | binding: 0, 438 | 439 | resource: { buffer: uniformBuffer } 440 | 441 | } ] 442 | 443 | } ); 444 | 445 | this.renderPassEncoder.setBindGroup( 0, uniformBindGroup ); 446 | 447 | } 448 | 449 | 450 | public Draw( indexCount: number ) { 451 | 452 | this.renderPassEncoder.drawIndexed( indexCount, 1, 0, 0, 0 ); 453 | 454 | } 455 | 456 | public Present() { 457 | 458 | this.renderPassEncoder.end(); 459 | 460 | this.device.queue.submit( [ this.commandEncoder.finish() ] ); 461 | 462 | } 463 | 464 | } -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 2 - Add Color 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | 6 | const triangleVertex = new Float32Array( [ 7 | 8 | // position // color 9 | 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 10 | -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 11 | 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0 12 | 13 | ] ); 14 | 15 | const triangleVertexPositon = new Float32Array( [ 16 | 17 | 0.0, 1.0, 0.0, 18 | -1.0, -1.0, 0.0, 19 | 1.0, -1.0, 0.0, 20 | 21 | ] ); 22 | 23 | const triangleVertexColor = new Float32Array( [ 24 | 25 | 1.0, 0.0, 0.0, 1.0, 26 | 0.0, 1.0, 0.0, 1.0, 27 | 0.0, 0.0, 1.0, 1.0 28 | 29 | ] ); 30 | 31 | const triangleIndex = new Uint32Array( [ 0, 1, 2 ] ); 32 | 33 | const triangleMVMatrix = new Matrix4().makeTranslation( -1.5, 0.0, -7.0 ); 34 | 35 | const squareVertex = new Float32Array( [ 36 | 37 | // position // color 38 | 1.0, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0, 39 | -1.0, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0, 40 | 1.0, -1.0, 0.0, 0.5, 0.5, 1.0, 1.0, 41 | -1.0, -1.0, 0.0, 0.5, 0.5, 1.0, 1.0 42 | 43 | ] ); 44 | 45 | const squareVertexPosition = new Float32Array( [ 46 | 47 | 1.0, 1.0, 0.0, 48 | -1.0, 1.0, 0.0, 49 | 1.0, -1.0, 0.0, 50 | -1.0, -1.0, 0.0 51 | 52 | ] ); 53 | 54 | const squareVertexColor = new Float32Array( [ 55 | 56 | 0.5, 0.5, 1.0, 1.0 57 | 58 | ] ); 59 | 60 | const squareIndex = new Uint32Array( [ 0, 1, 2, 1, 2, 3 ] ); 61 | 62 | const squareMVMatrix = new Matrix4().makeTranslation( 1.5, 0.0, -7.0 ); 63 | 64 | let main = async () => { 65 | 66 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 67 | 68 | let pMatrix = camera.projectionMatrix; 69 | 70 | let triangleUniformBufferView = new Float32Array( pMatrix.toArray().concat( triangleMVMatrix.toArray() ) ); 71 | 72 | let squareUniformBufferView = new Float32Array( pMatrix.toArray().concat( squareMVMatrix.toArray() ) ); 73 | 74 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 75 | 76 | let app = new App(); 77 | 78 | app.CreateCanvas( document.body ); 79 | 80 | await app.InitWebGPU(); 81 | 82 | app.InitRenderPass( backgroundColor ); 83 | 84 | app.InitPipeline( vxCode, fxCode ); 85 | 86 | app.InitGPUBuffer( triangleVertex, triangleIndex, triangleUniformBufferView ); 87 | 88 | app.Draw( triangleIndex.length ); 89 | 90 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 91 | 92 | app.InitGPUBufferWithMultiBuffers( squareVertexPosition, squareVertexColor, squareIndex, squareUniformBufferView ); 93 | 94 | app.Draw( squareIndex.length ); 95 | 96 | app.Present(); 97 | 98 | } 99 | 100 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@stage(fragment) 3 | fn main( 4 | @location(0) vColor: vec4 5 | ) -> @location(0) vec4 { 6 | return vColor; 7 | }`; -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vColor: vec4; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @location(0) aVertexPosition: vec3, 18 | @location(1) aVertexColor: vec4 19 | ) -> Output { 20 | var output: Output; 21 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 22 | output.vColor = aVertexColor; 23 | return output; 24 | }`; -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson2_Add_Color/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson2_Add_Color/Tutorial/image/gta5_out_of_memory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson2_Add_Color/Tutorial/image/gta5_out_of_memory.jpg -------------------------------------------------------------------------------- /Lesson2_Add_Color/Tutorial/image/lesson2_add_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson2_Add_Color/Tutorial/image/lesson2_add_colors.png -------------------------------------------------------------------------------- /Lesson3_Animate/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson3_Animate/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson3_Animate/Code/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson3-animate", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "lesson3-animate", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@webgpu/glslang": "0.0.12", 12 | "@webgpu/types": "^0.1.13", 13 | "three": "^0.125.2" 14 | }, 15 | "devDependencies": { 16 | "vite": "^2.3.8" 17 | } 18 | }, 19 | "node_modules/@webgpu/glslang": { 20 | "version": "0.0.12", 21 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 22 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 23 | }, 24 | "node_modules/@webgpu/types": { 25 | "version": "0.1.13", 26 | "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.13.tgz", 27 | "integrity": "sha512-SAq8FRONvMANQi/eXw5ArKfSvih6am/EC+5y7+du2xf1VyprtKn4ylUPKGW4T6ZkDogtH3xZgGE+J/cx601L5w==" 28 | }, 29 | "node_modules/colorette": { 30 | "version": "1.2.2", 31 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 32 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 33 | "dev": true, 34 | "license": "MIT" 35 | }, 36 | "node_modules/esbuild": { 37 | "version": "0.12.9", 38 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 39 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 40 | "dev": true, 41 | "hasInstallScript": true, 42 | "license": "MIT", 43 | "bin": { 44 | "esbuild": "bin/esbuild" 45 | } 46 | }, 47 | "node_modules/fsevents": { 48 | "version": "2.3.2", 49 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 50 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 51 | "dev": true, 52 | "optional": true, 53 | "os": [ 54 | "darwin" 55 | ], 56 | "engines": { 57 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 58 | } 59 | }, 60 | "node_modules/function-bind": { 61 | "version": "1.1.1", 62 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 63 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 64 | "dev": true 65 | }, 66 | "node_modules/has": { 67 | "version": "1.0.3", 68 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 69 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 70 | "dev": true, 71 | "dependencies": { 72 | "function-bind": "^1.1.1" 73 | }, 74 | "engines": { 75 | "node": ">= 0.4.0" 76 | } 77 | }, 78 | "node_modules/is-core-module": { 79 | "version": "2.2.0", 80 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 81 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 82 | "dev": true, 83 | "dependencies": { 84 | "has": "^1.0.3" 85 | } 86 | }, 87 | "node_modules/nanoid": { 88 | "version": "3.3.0", 89 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 90 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 91 | "dev": true, 92 | "bin": { 93 | "nanoid": "bin/nanoid.cjs" 94 | }, 95 | "engines": { 96 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 97 | } 98 | }, 99 | "node_modules/path-parse": { 100 | "version": "1.0.7", 101 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 102 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 103 | "dev": true 104 | }, 105 | "node_modules/postcss": { 106 | "version": "8.3.5", 107 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 108 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 109 | "dev": true, 110 | "license": "MIT", 111 | "dependencies": { 112 | "colorette": "^1.2.2", 113 | "nanoid": "^3.1.23", 114 | "source-map-js": "^0.6.2" 115 | }, 116 | "engines": { 117 | "node": "^10 || ^12 || >=14" 118 | }, 119 | "funding": { 120 | "type": "opencollective", 121 | "url": "https://opencollective.com/postcss/" 122 | } 123 | }, 124 | "node_modules/rollup": { 125 | "version": "2.39.0", 126 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 127 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 128 | "dev": true, 129 | "bin": { 130 | "rollup": "dist/bin/rollup" 131 | }, 132 | "engines": { 133 | "node": ">=10.0.0" 134 | }, 135 | "optionalDependencies": { 136 | "fsevents": "~2.3.1" 137 | } 138 | }, 139 | "node_modules/source-map-js": { 140 | "version": "0.6.2", 141 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 142 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 143 | "dev": true, 144 | "license": "BSD-3-Clause", 145 | "engines": { 146 | "node": ">=0.10.0" 147 | } 148 | }, 149 | "node_modules/three": { 150 | "version": "0.125.2", 151 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 152 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 153 | }, 154 | "node_modules/vite": { 155 | "version": "2.3.8", 156 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 157 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 158 | "dev": true, 159 | "license": "MIT", 160 | "dependencies": { 161 | "esbuild": "^0.12.8", 162 | "postcss": "^8.3.4", 163 | "resolve": "^1.20.0", 164 | "rollup": "^2.38.5" 165 | }, 166 | "bin": { 167 | "vite": "bin/vite.js" 168 | }, 169 | "engines": { 170 | "node": ">=12.0.0" 171 | }, 172 | "optionalDependencies": { 173 | "fsevents": "~2.3.2" 174 | } 175 | }, 176 | "node_modules/vite/node_modules/resolve": { 177 | "version": "1.20.0", 178 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 179 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 180 | "dev": true, 181 | "dependencies": { 182 | "is-core-module": "^2.2.0", 183 | "path-parse": "^1.0.6" 184 | } 185 | } 186 | }, 187 | "dependencies": { 188 | "@webgpu/glslang": { 189 | "version": "0.0.12", 190 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 191 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 192 | }, 193 | "@webgpu/types": { 194 | "version": "0.1.13", 195 | "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.13.tgz", 196 | "integrity": "sha512-SAq8FRONvMANQi/eXw5ArKfSvih6am/EC+5y7+du2xf1VyprtKn4ylUPKGW4T6ZkDogtH3xZgGE+J/cx601L5w==" 197 | }, 198 | "colorette": { 199 | "version": "1.2.2", 200 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 201 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 202 | "dev": true 203 | }, 204 | "esbuild": { 205 | "version": "0.12.9", 206 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 207 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 208 | "dev": true 209 | }, 210 | "fsevents": { 211 | "version": "2.3.2", 212 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 213 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 214 | "dev": true, 215 | "optional": true 216 | }, 217 | "function-bind": { 218 | "version": "1.1.1", 219 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 220 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 221 | "dev": true 222 | }, 223 | "has": { 224 | "version": "1.0.3", 225 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 226 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 227 | "dev": true, 228 | "requires": { 229 | "function-bind": "^1.1.1" 230 | } 231 | }, 232 | "is-core-module": { 233 | "version": "2.2.0", 234 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 235 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 236 | "dev": true, 237 | "requires": { 238 | "has": "^1.0.3" 239 | } 240 | }, 241 | "nanoid": { 242 | "version": "3.3.0", 243 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 244 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 245 | "dev": true 246 | }, 247 | "path-parse": { 248 | "version": "1.0.7", 249 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 250 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 251 | "dev": true 252 | }, 253 | "postcss": { 254 | "version": "8.3.5", 255 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 256 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 257 | "dev": true, 258 | "requires": { 259 | "colorette": "^1.2.2", 260 | "nanoid": "^3.1.23", 261 | "source-map-js": "^0.6.2" 262 | } 263 | }, 264 | "rollup": { 265 | "version": "2.39.0", 266 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 267 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 268 | "dev": true, 269 | "requires": { 270 | "fsevents": "~2.3.1" 271 | } 272 | }, 273 | "source-map-js": { 274 | "version": "0.6.2", 275 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 276 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 277 | "dev": true 278 | }, 279 | "three": { 280 | "version": "0.125.2", 281 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 282 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 283 | }, 284 | "vite": { 285 | "version": "2.3.8", 286 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 287 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 288 | "dev": true, 289 | "requires": { 290 | "esbuild": "^0.12.8", 291 | "fsevents": "~2.3.2", 292 | "postcss": "^8.3.4", 293 | "resolve": "^1.20.0", 294 | "rollup": "^2.38.5" 295 | }, 296 | "dependencies": { 297 | "resolve": { 298 | "version": "1.20.0", 299 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 300 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 301 | "dev": true, 302 | "requires": { 303 | "is-core-module": "^2.2.0", 304 | "path-parse": "^1.0.6" 305 | } 306 | } 307 | } 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /Lesson3_Animate/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson3-animate", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson3_Animate/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from 'three'; 2 | 3 | export class App { 4 | 5 | public canvas: HTMLCanvasElement; 6 | 7 | public adapter: GPUAdapter; 8 | 9 | public device: GPUDevice; 10 | 11 | public context: GPUCanvasContext; 12 | 13 | public format: GPUTextureFormat = 'bgra8unorm'; 14 | 15 | public commandEncoder: GPUCommandEncoder; 16 | 17 | public renderPassEncoder: GPURenderPassEncoder; 18 | 19 | public uniformGroupLayout: GPUBindGroupLayout; 20 | 21 | public renderPipeline: GPURenderPipeline; 22 | 23 | public devicePixelWidth: number; 24 | 25 | public devicePixelHeight: number; 26 | 27 | private _clearColor: GPUColorDict; 28 | 29 | public CreateCanvas( rootElement: HTMLElement ) { 30 | 31 | let width = rootElement.clientWidth; 32 | 33 | let height = rootElement.clientHeight; 34 | 35 | this.devicePixelWidth = width * window.devicePixelRatio; 36 | 37 | this.devicePixelHeight = height * window.devicePixelRatio; 38 | 39 | this.canvas = document.createElement( 'canvas' ); 40 | 41 | this.canvas.width = this.devicePixelWidth; 42 | 43 | this.canvas.height = this.devicePixelHeight; 44 | 45 | this.canvas.style.width = '100%'; 46 | 47 | this.canvas.style.height = '100%'; 48 | 49 | rootElement.appendChild( this.canvas ); 50 | 51 | } 52 | 53 | public async InitWebGPU() { 54 | 55 | this.adapter = await navigator.gpu.requestAdapter( { 56 | 57 | powerPreference: 'high-performance' 58 | 59 | } ); 60 | 61 | this.device = await this.adapter.requestDevice(); 62 | 63 | this.context = this.canvas.getContext( 'webgpu' ) as GPUCanvasContext; 64 | 65 | this.format = this.context.getPreferredFormat( this.adapter ); 66 | 67 | this.context.configure( { 68 | 69 | device: this.device, 70 | 71 | format: this.format, 72 | 73 | usage: GPUTextureUsage.RENDER_ATTACHMENT 74 | 75 | } ); 76 | 77 | } 78 | 79 | public InitRenderPass( clearColor: GPUColorDict ) { 80 | 81 | this.commandEncoder = this.device.createCommandEncoder(); 82 | 83 | let renderPassDescriptor: GPURenderPassDescriptor = { 84 | 85 | colorAttachments: [ { 86 | 87 | view: this.context.getCurrentTexture().createView(), 88 | 89 | loadOp: 'clear', 90 | 91 | storeOp: 'store', 92 | 93 | clearValue: clearColor 94 | 95 | } ] 96 | 97 | } 98 | 99 | this.renderPassEncoder = this.commandEncoder.beginRenderPass( renderPassDescriptor ); 100 | 101 | if ( !this._clearColor ) { 102 | 103 | this._clearColor = clearColor; 104 | 105 | } 106 | 107 | this.renderPassEncoder.setViewport( 0, 0, this.devicePixelWidth, this.devicePixelHeight, 0, 1 ); 108 | 109 | } 110 | 111 | public InitPipelineWitMultiBuffers( vxCode: string, fxCode: string ) { 112 | 113 | this.uniformGroupLayout = this.device.createBindGroupLayout( { 114 | 115 | entries: [ 116 | 117 | { 118 | 119 | binding: 0, 120 | 121 | visibility: GPUShaderStage.VERTEX, 122 | 123 | buffer: { 124 | 125 | type: 'uniform' 126 | 127 | } 128 | 129 | } 130 | 131 | ] 132 | 133 | } ); 134 | 135 | let layout: GPUPipelineLayout = this.device.createPipelineLayout( { 136 | 137 | bindGroupLayouts: [ this.uniformGroupLayout ] 138 | 139 | } ); 140 | 141 | let vxModule: GPUShaderModule = this.device.createShaderModule( { 142 | 143 | code: vxCode 144 | 145 | } ); 146 | 147 | let fxModule: GPUShaderModule = this.device.createShaderModule( { 148 | 149 | code: fxCode 150 | 151 | } ); 152 | 153 | this.renderPipeline = this.device.createRenderPipeline( { 154 | 155 | layout: layout, 156 | 157 | vertex: { 158 | 159 | buffers: [ 160 | 161 | { 162 | 163 | arrayStride: 4 * 3, 164 | 165 | attributes: [ 166 | 167 | // position 168 | 169 | { 170 | 171 | shaderLocation: 0, 172 | 173 | offset: 0, 174 | 175 | format: 'float32x3' 176 | 177 | } 178 | 179 | ], 180 | 181 | stepMode: 'vertex' 182 | 183 | }, 184 | 185 | { 186 | 187 | arrayStride: 4 * 4, 188 | 189 | attributes: [ 190 | 191 | // color 192 | 193 | { 194 | 195 | shaderLocation: 1, 196 | 197 | offset: 0, 198 | 199 | format: 'float32x4' 200 | 201 | } 202 | 203 | ], 204 | 205 | stepMode: 'vertex' 206 | 207 | }, 208 | 209 | ], 210 | 211 | module: vxModule, 212 | 213 | entryPoint: 'main' 214 | 215 | }, 216 | 217 | fragment: { 218 | 219 | module: fxModule, 220 | 221 | entryPoint: 'main', 222 | 223 | targets: [ 224 | 225 | { 226 | 227 | format: this.format 228 | 229 | } 230 | 231 | ] 232 | 233 | }, 234 | 235 | primitive: { 236 | 237 | topology: 'triangle-list' 238 | 239 | } 240 | 241 | } ); 242 | 243 | this.renderPassEncoder.setPipeline( this.renderPipeline ); 244 | 245 | } 246 | 247 | private _CreateGPUBuffer( typedArray: TypedArray, usage: GPUBufferUsageFlags ) { 248 | 249 | let gpuBuffer = this.device.createBuffer( { 250 | 251 | size: typedArray.byteLength, 252 | 253 | usage: usage | GPUBufferUsage.COPY_DST, 254 | 255 | mappedAtCreation: true 256 | 257 | } ); 258 | 259 | let constructor = typedArray.constructor as new ( buffer: ArrayBuffer ) => TypedArray; 260 | 261 | let view = new constructor( gpuBuffer.getMappedRange() ); 262 | 263 | view.set( typedArray, 0 ); 264 | 265 | gpuBuffer.unmap(); 266 | 267 | return gpuBuffer; 268 | 269 | } 270 | 271 | public InitGPUBufferWithMultiBuffers( vxArray: Float32Array, colorArray: Float32Array, idxArray: Uint32Array, mxArray: Float32Array ) { 272 | 273 | let vertexBuffer = this._CreateGPUBuffer( vxArray, GPUBufferUsage.VERTEX ); 274 | 275 | this.renderPassEncoder.setVertexBuffer( 0, vertexBuffer ); 276 | 277 | let colorBuffer = this._CreateGPUBuffer( colorArray, GPUBufferUsage.VERTEX ); 278 | 279 | this.renderPassEncoder.setVertexBuffer( 1, colorBuffer, 0 ); 280 | 281 | let indexBuffer = this._CreateGPUBuffer( idxArray, GPUBufferUsage.INDEX ); 282 | 283 | this.renderPassEncoder.setIndexBuffer( indexBuffer, "uint32" ); 284 | 285 | let uniformBuffer = this._CreateGPUBuffer( mxArray, GPUBufferUsage.UNIFORM ); 286 | 287 | let uniformBindGroup = this.device.createBindGroup( { 288 | 289 | layout: this.uniformGroupLayout, 290 | 291 | entries: [ { 292 | 293 | binding: 0, 294 | 295 | resource: { buffer: uniformBuffer } 296 | 297 | } ] 298 | 299 | } ); 300 | 301 | this.renderPassEncoder.setBindGroup( 0, uniformBindGroup ); 302 | 303 | return { uniformBuffer }; 304 | 305 | } 306 | 307 | public Draw( indexCount: number ) { 308 | 309 | this.renderPassEncoder.drawIndexed( indexCount, 1, 0, 0, 0 ); 310 | 311 | } 312 | 313 | public Present() { 314 | 315 | this.renderPassEncoder.end(); 316 | 317 | this.device.queue.submit( [ this.commandEncoder.finish() ] ); 318 | 319 | } 320 | 321 | public RunRenderLoop( fn: Function ) { 322 | 323 | fn(); 324 | 325 | requestAnimationFrame( () => this.RunRenderLoop( fn ) ); 326 | 327 | } 328 | 329 | } -------------------------------------------------------------------------------- /Lesson3_Animate/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 3 - Animate 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson3_Animate/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | 6 | const triangleVertexPositon = new Float32Array( [ 7 | 8 | 0.0, 1.0, 0.0, 9 | -1.0, -1.0, 0.0, 10 | 1.0, -1.0, 0.0, 11 | 12 | ] ); 13 | 14 | const triangleVertexColor = new Float32Array( [ 15 | 16 | 1.0, 0.0, 0.0, 1.0, 17 | 0.0, 1.0, 0.0, 1.0, 18 | 0.0, 0.0, 1.0, 1.0 19 | 20 | ] ); 21 | 22 | const triangleIndex = new Uint32Array( [ 0, 1, 2 ] ); 23 | 24 | const triangleMVMatrix = new Matrix4(); 25 | 26 | const squareVertexPosition = new Float32Array( [ 27 | 28 | 1.0, 1.0, 0.0, 29 | -1.0, 1.0, 0.0, 30 | 1.0, -1.0, 0.0, 31 | -1.0, -1.0, 0.0 32 | 33 | ] ); 34 | 35 | const squareVertexColor = new Float32Array( [ 36 | 37 | 0.5, 0.5, 1.0, 1.0, 38 | 0.5, 0.5, 1.0, 1.0, 39 | 0.5, 0.5, 1.0, 1.0, 40 | 0.5, 0.5, 1.0, 1.0, 41 | 42 | ] ); 43 | 44 | const squareIndex = new Uint32Array( [ 0, 1, 2, 1, 2, 3 ] ); 45 | 46 | const squareMVMatrix = new Matrix4(); 47 | 48 | let main = async () => { 49 | 50 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 51 | 52 | let pMatrix = camera.projectionMatrix; 53 | 54 | 55 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 56 | 57 | let app = new App(); 58 | 59 | app.CreateCanvas( document.body ) 60 | 61 | await app.InitWebGPU(); 62 | 63 | app.InitRenderPass( backgroundColor ); 64 | 65 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 66 | 67 | let lastTime = 0, rTri = 0, rSquare = 0; 68 | 69 | let animate = () => { 70 | 71 | let timeNow = performance.now(); 72 | 73 | if ( lastTime != 0 ) { 74 | 75 | let elapsed = timeNow - lastTime; 76 | 77 | rTri += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 78 | 79 | rSquare += ( Math.PI / 180 * 75 * elapsed ) / 1000.0; 80 | 81 | } 82 | 83 | lastTime = timeNow; 84 | } 85 | 86 | app.RunRenderLoop( () => { 87 | 88 | animate(); 89 | 90 | app.InitRenderPass( backgroundColor ); 91 | 92 | app.renderPassEncoder.setPipeline( app.renderPipeline ); 93 | 94 | triangleMVMatrix.makeTranslation( -1.5, 0.0, -7.0 ).multiply( new Matrix4().makeRotationY( rTri ) ); 95 | 96 | squareMVMatrix.makeTranslation( 1.5, 0.0, -7.0 ).multiply( new Matrix4().makeRotationX( rSquare ) ); 97 | 98 | let triangleUniformBufferView = new Float32Array( pMatrix.toArray().concat( triangleMVMatrix.toArray() ) ); 99 | 100 | let squareUniformBufferView = new Float32Array( pMatrix.toArray().concat( squareMVMatrix.toArray() ) ); 101 | 102 | app.InitGPUBufferWithMultiBuffers( triangleVertexPositon, triangleVertexColor, triangleIndex, triangleUniformBufferView ); 103 | 104 | app.Draw( triangleIndex.length ); 105 | 106 | app.InitGPUBufferWithMultiBuffers( squareVertexPosition, squareVertexColor, squareIndex, squareUniformBufferView ); 107 | 108 | app.Draw( squareIndex.length ); 109 | 110 | app.Present(); 111 | 112 | }) 113 | 114 | 115 | } 116 | 117 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson3_Animate/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@stage(fragment) 3 | fn main( 4 | @location(0) vColor: vec4 5 | ) -> @location(0) vec4 { 6 | return vColor; 7 | }`; -------------------------------------------------------------------------------- /Lesson3_Animate/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vColor: vec4; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @location(0) aVertexPosition: vec3, 18 | @location(1) aVertexColor: vec4 19 | ) -> Output { 20 | var output: Output; 21 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 22 | output.vColor = aVertexColor; 23 | return output; 24 | }`; -------------------------------------------------------------------------------- /Lesson3_Animate/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson3_Animate/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson3_Animate/Tutorial/Lesson3_Animate.md: -------------------------------------------------------------------------------- 1 | # Lesson3 动起来 2 | 3 | ## 教程说明 4 | 5 | 欢迎来到 LearningWebGPU 教程,本教程改写自原本的 LearningWebGL.com 的 WebGL 入门教程,而后者则是改编于经典的 Nehe 的 OpenGL 教程。在开始教程前,有一些大家需要知道的事情: 6 | 7 | 1. 目前 WebGPU 仍然处于草稿阶段,所有的 API 接口可能会在未来发生变化。 8 | 9 | 2. 目前浏览器对于 WebGPU 的实现和支持仍处于实验阶段,可能会发生一些因为浏览器实现导致的 Bug 或错误;另外,开启浏览器的试验功能可能会降低浏览器的安全性,所以你不应该使用学习 WebGPU 开发的浏览器作为主力浏览器,例如请不要在这个浏览器中浏览、输入个人隐私信息、不要进行网页支付等。 10 | 11 | 3. 考虑到 WebGPU 正式投入生产环境应该是数年后的事情,所以本教程中将会使用大量新的 ECMA Script 的新特性,这些特性可能并不被当下的浏览器和 JavaScript 引擎所支持。 12 | 13 | 4. 本系列的教程是针对那些已经具备相应编程知识但没有实际 3D 图形经验的人的;目标是让学习者创建并运行代码,并且明白代码其中的含义,从而快速地创建自己的 3D Web 页面。 14 | 15 | 5. 我编写这套教程是因为我在独立学习 WebGPU,所以教程中可能(非常可能)会有错误,所以还请风险自担。尽管如此,我还是会不断的修正 Bug 和改正其中的错误的,所以如果你发现了教程中的错误或其他任何改进建议,请在本教程的 Github 仓库的 Issue 页面提交。 16 | 17 | 在这节课中,我们将实现以下效果: 18 | 19 | ![Lesson3 动起来](./image/animate.gif) 20 | 21 | 下面让我们开始第 3 课的内容。 22 | 23 | 在前面的课程中,我们基本上可以通过所学的知识,在屏幕上绘制出简单图形了;但是,它们都是静止不动的图形。原因在于,我们在代码中只渲染了 1 帧图形。 24 | 25 | ```typescript 26 | app.Draw( squareIndex.length ); 27 | 28 | app.Present(); 29 | ``` 30 | 31 | 可以看到,我们执行了绘制和呈现命令后,WebGPU 将我们绘制的图形呈现在屏幕上,但是同时代码也终止于此。那么,如何才能让绘制的图形动起来呢? 32 | 33 | 首先,在 WebGPU 中绘制运动图形的原理和在 WebGL 中是一样的,就是不断重复绘制那个图形,在每次绘制中,都稍微改变一下图形的某个属性,例如位置、旋转、缩放等等。 34 | 35 | 和大多数图形底层框架一样,WebGPU 并没有提供一个高级的循环渲染接口,因此我们需要借助于 JavaScript 和浏览器环境中的相关 API 来实现循环渲染。 36 | 37 | 这就是大家熟知的 `window.requestAnimationFrame()`。在本教程的早期版本中,仍需解释为何使用这个 API 而不是 JavaScript 中的 `setInterval()`,但相信在 2020 年的版本中,读者一定都已经熟知其中的原因了。顺便说一句,在 `requestAnimationFrame()` 出现前,大概是 10 年前左右,WebGL 程序的确是使用 `setInterval()` 来循环渲染的。 38 | 39 | 让我们看一下本课的 `app.ts`,我们为 `App` 类新增了一个名为 `RunRenderLoop()` 的方法,在这个函数中,我们接受一个函数作为参数,并执行这个函数,然后调用 `requestAnimationFrame()` 来进行循环渲染。 40 | 41 | ```typescript 42 | public RunRenderLoop( fn: Function ) { 43 | 44 | fn(); 45 | 46 | requestAnimationFrame( () => this.RunRenderLoop( fn ) ); 47 | 48 | } 49 | ``` 50 | 51 | 接下来,我们来看看 `main.ts` 中的代码,这也是我们在 HTML 网页中执行的主程序。在这里,我们新增了一个名为 `animate()` 的函数。 52 | 53 | ```typescript 54 | let lastTime = 0, rTri = 0, rSquare = 0; 55 | 56 | let animate = () => { 57 | 58 | let timeNow = performance.now(); 59 | 60 | if ( lastTime != 0 ) { 61 | 62 | let elapsed = timeNow - lastTime; 63 | 64 | rTri += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 65 | 66 | rSquare += ( Math.PI / 180 * 75 * elapsed ) / 1000.0; 67 | 68 | } 69 | 70 | lastTime = timeNow; 71 | } 72 | ``` 73 | 74 | 我们在这里基本就是计算了一个时间差,并根据时间差计算出在这段时间间隔内图形的运动量。这样做的好处就是,无论在性能如何的电脑上,图形在单位时间内的运动量都会保持一致。 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Lesson3_Animate/Tutorial/image/animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson3_Animate/Tutorial/image/animate.gif -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson4-something-real-3d", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from 'three'; 2 | 3 | export class App { 4 | 5 | public canvas: HTMLCanvasElement; 6 | 7 | public adapter: GPUAdapter; 8 | 9 | public device: GPUDevice; 10 | 11 | public context: GPUCanvasContext; 12 | 13 | public format: GPUTextureFormat = 'bgra8unorm'; 14 | 15 | public commandEncoder: GPUCommandEncoder; 16 | 17 | public renderPassEncoder: GPURenderPassEncoder; 18 | 19 | public uniformGroupLayout: GPUBindGroupLayout; 20 | 21 | public renderPipeline: GPURenderPipeline; 22 | 23 | public devicePixelWidth: number; 24 | 25 | public devicePixelHeight: number; 26 | 27 | private _clearColor: GPUColorDict; 28 | 29 | public async CreateCanvas( rootElement: HTMLElement ) { 30 | 31 | let width = rootElement.clientWidth; 32 | 33 | let height = rootElement.clientHeight; 34 | 35 | this.devicePixelWidth = width * window.devicePixelRatio; 36 | 37 | this.devicePixelHeight = height * window.devicePixelRatio; 38 | 39 | this.canvas = document.createElement( 'canvas' ); 40 | 41 | this.canvas.width = this.devicePixelWidth; 42 | 43 | this.canvas.height = this.devicePixelHeight; 44 | 45 | this.canvas.style.width = '100%'; 46 | 47 | this.canvas.style.height = '100%'; 48 | 49 | rootElement.appendChild( this.canvas ); 50 | 51 | return Promise.resolve( { 52 | 53 | width: this.devicePixelWidth, 54 | height: this.devicePixelHeight 55 | 56 | } ); 57 | 58 | } 59 | 60 | public async InitWebGPU( width: number, height: number ) { 61 | 62 | this.adapter = await navigator.gpu.requestAdapter( { 63 | 64 | powerPreference: 'high-performance' 65 | 66 | } ); 67 | 68 | this.device = await this.adapter.requestDevice(); 69 | 70 | this.context = this.canvas.getContext( 'webgpu' ) as GPUCanvasContext; 71 | 72 | this.format = this.context.getPreferredFormat( this.adapter ); 73 | 74 | this.context.configure( { 75 | 76 | device: this.device, 77 | 78 | format: this.format, 79 | 80 | usage: GPUTextureUsage.RENDER_ATTACHMENT 81 | 82 | } ); 83 | 84 | let colorTexture = this.device.createTexture( { 85 | 86 | size: { 87 | 88 | width, 89 | 90 | height, 91 | 92 | depthOrArrayLayers: 1 93 | 94 | }, 95 | 96 | sampleCount: 4, 97 | 98 | format: this.format, 99 | 100 | usage: GPUTextureUsage.RENDER_ATTACHMENT 101 | 102 | } ); 103 | 104 | let colorAttachmentView = colorTexture.createView(); 105 | 106 | let depthStencilTexture = this.device.createTexture( { 107 | 108 | size: { 109 | 110 | width, 111 | 112 | height, 113 | 114 | depthOrArrayLayers: 1 115 | 116 | }, 117 | 118 | sampleCount: 4, 119 | 120 | format: 'depth24plus-stencil8', 121 | 122 | usage: GPUTextureUsage.RENDER_ATTACHMENT 123 | 124 | } ); 125 | 126 | let depthStencilAttachmentView = depthStencilTexture.createView(); 127 | 128 | return Promise.resolve( { colorAttachmentView, depthStencilAttachmentView } ); 129 | 130 | } 131 | 132 | public InitRenderPass( clearColor: GPUColorDict, colorAttachmentView: GPUTextureView, depthStencilAttachmentView: GPUTextureView ) { 133 | 134 | this.commandEncoder = this.device.createCommandEncoder(); 135 | 136 | let renderPassDescriptor: GPURenderPassDescriptor = { 137 | 138 | colorAttachments: [ { 139 | 140 | view: colorAttachmentView, 141 | 142 | resolveTarget: this.context.getCurrentTexture().createView(), 143 | 144 | clearValue: clearColor, 145 | 146 | storeOp: 'store', 147 | 148 | loadOp: 'clear' 149 | 150 | } ], 151 | 152 | depthStencilAttachment: { 153 | 154 | view: depthStencilAttachmentView, 155 | 156 | depthLoadOp: 'clear', 157 | 158 | depthClearValue: 1.0, 159 | 160 | depthStoreOp: 'store', 161 | 162 | stencilClearValue: 1.0, 163 | 164 | stencilLoadOp: 'clear', 165 | 166 | stencilStoreOp: 'store' 167 | 168 | } 169 | 170 | } 171 | 172 | this.renderPassEncoder = this.commandEncoder.beginRenderPass( renderPassDescriptor ); 173 | 174 | if ( !this._clearColor ) { 175 | 176 | this._clearColor = clearColor; 177 | 178 | } 179 | 180 | this.renderPassEncoder.setViewport( 0, 0, this.devicePixelWidth, this.devicePixelHeight, 0, 1 ); 181 | 182 | } 183 | 184 | public InitPipelineWitMultiBuffers( vxCode: string, fxCode: string ) { 185 | 186 | this.uniformGroupLayout = this.device.createBindGroupLayout( { 187 | 188 | entries: [ 189 | 190 | { 191 | 192 | binding: 0, 193 | 194 | visibility: GPUShaderStage.VERTEX, 195 | 196 | buffer: { 197 | 198 | type: 'uniform' 199 | 200 | } 201 | 202 | } 203 | 204 | ] 205 | 206 | } ); 207 | 208 | let layout: GPUPipelineLayout = this.device.createPipelineLayout( { 209 | 210 | bindGroupLayouts: [ this.uniformGroupLayout ] 211 | 212 | } ); 213 | 214 | let vxModule: GPUShaderModule = this.device.createShaderModule( { 215 | 216 | code: vxCode 217 | 218 | } ); 219 | 220 | let fxModule: GPUShaderModule = this.device.createShaderModule( { 221 | 222 | code: fxCode 223 | 224 | } ); 225 | 226 | this.renderPipeline = this.device.createRenderPipeline( { 227 | 228 | layout: layout, 229 | 230 | vertex: { 231 | 232 | buffers: [ 233 | 234 | { 235 | 236 | arrayStride: 4 * 3, 237 | 238 | attributes: [ 239 | 240 | // position 241 | 242 | { 243 | 244 | shaderLocation: 0, 245 | 246 | offset: 0, 247 | 248 | format: 'float32x3' 249 | 250 | } 251 | 252 | ], 253 | 254 | stepMode: 'vertex' 255 | 256 | }, 257 | 258 | { 259 | 260 | arrayStride: 4 * 4, 261 | 262 | attributes: [ 263 | 264 | // color 265 | 266 | { 267 | 268 | shaderLocation: 1, 269 | 270 | offset: 0, 271 | 272 | format: 'float32x4' 273 | 274 | } 275 | 276 | ], 277 | 278 | stepMode: 'vertex' 279 | 280 | } 281 | 282 | ], 283 | 284 | module: vxModule, 285 | 286 | entryPoint: 'main' 287 | 288 | }, 289 | 290 | fragment: { 291 | 292 | module: fxModule, 293 | 294 | entryPoint: 'main', 295 | 296 | targets: [ 297 | 298 | { 299 | 300 | format: this.format 301 | 302 | } 303 | 304 | ] 305 | 306 | }, 307 | 308 | primitive: { 309 | 310 | topology: 'triangle-list' 311 | 312 | }, 313 | 314 | depthStencil: { 315 | 316 | depthWriteEnabled: true, 317 | 318 | depthCompare: 'less', 319 | 320 | format: 'depth24plus-stencil8' 321 | 322 | }, 323 | 324 | multisample: { 325 | 326 | count: 4 327 | 328 | } 329 | 330 | } ); 331 | 332 | this.renderPassEncoder.setPipeline( this.renderPipeline ); 333 | 334 | } 335 | 336 | private _CreateGPUBuffer( typedArray: TypedArray, usage: GPUBufferUsageFlags ) { 337 | 338 | let gpuBuffer = this.device.createBuffer( { 339 | 340 | size: typedArray.byteLength, 341 | 342 | usage: usage | GPUBufferUsage.COPY_DST, 343 | 344 | mappedAtCreation: true 345 | 346 | } ); 347 | 348 | let constructor = typedArray.constructor as new ( buffer: ArrayBuffer ) => TypedArray; 349 | 350 | let view = new constructor( gpuBuffer.getMappedRange() ); 351 | 352 | view.set( typedArray, 0 ); 353 | 354 | gpuBuffer.unmap(); 355 | 356 | return gpuBuffer; 357 | 358 | } 359 | 360 | public InitGPUBufferWithMultiBuffers( vxArray: Float32Array, colorArray: Float32Array, mxArray: Float32Array, idxArray?: Uint32Array ) { 361 | 362 | let vertexBuffer = this._CreateGPUBuffer( vxArray, GPUBufferUsage.VERTEX ); 363 | 364 | this.renderPassEncoder.setVertexBuffer( 0, vertexBuffer ); 365 | 366 | let colorBuffer = this._CreateGPUBuffer( colorArray, GPUBufferUsage.VERTEX ); 367 | 368 | this.renderPassEncoder.setVertexBuffer( 1, colorBuffer, 0 ); 369 | 370 | if ( idxArray ) { 371 | 372 | let indexBuffer = this._CreateGPUBuffer( idxArray, GPUBufferUsage.INDEX ); 373 | 374 | this.renderPassEncoder.setIndexBuffer( indexBuffer, "uint32" ); 375 | 376 | } 377 | 378 | let uniformBuffer = this._CreateGPUBuffer( mxArray, GPUBufferUsage.UNIFORM ); 379 | 380 | let uniformBindGroup = this.device.createBindGroup( { 381 | 382 | layout: this.uniformGroupLayout, 383 | 384 | entries: [ { 385 | 386 | binding: 0, 387 | 388 | resource: { buffer: uniformBuffer } 389 | 390 | } ] 391 | 392 | } ); 393 | 394 | this.renderPassEncoder.setBindGroup( 0, uniformBindGroup ); 395 | 396 | return { uniformBuffer }; 397 | 398 | } 399 | 400 | public DrawIndexed( indexCount: number ) { 401 | 402 | this.renderPassEncoder.drawIndexed( indexCount, 1, 0, 0, 0 ); 403 | 404 | } 405 | 406 | public Draw( vertexCount: number ) { 407 | 408 | this.renderPassEncoder.draw( vertexCount, 1, 0, 0 ); 409 | 410 | } 411 | 412 | public Present() { 413 | 414 | this.renderPassEncoder.end(); 415 | 416 | this.device.queue.submit( [ this.commandEncoder.finish() ] ); 417 | 418 | } 419 | 420 | public RunRenderLoop( fn: Function ) { 421 | 422 | fn(); 423 | 424 | requestAnimationFrame( () => this.RunRenderLoop( fn ) ); 425 | 426 | } 427 | 428 | } -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 4 - Something real 3D 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | 6 | const pyramidVertexPositon = new Float32Array( [ 7 | 8 | // Front face 9 | 0.0, 1.0, 0.0, 10 | -1.0, -1.0, 1.0, 11 | 1.0, -1.0, 1.0, 12 | 13 | // Right face 14 | 0.0, 1.0, 0.0, 15 | 1.0, -1.0, 1.0, 16 | 1.0, -1.0, -1.0, 17 | 18 | // Back face 19 | 0.0, 1.0, 0.0, 20 | 1.0, -1.0, -1.0, 21 | -1.0, -1.0, -1.0, 22 | 23 | // Left face 24 | 0.0, 1.0, 0.0, 25 | -1.0, -1.0, -1.0, 26 | -1.0, -1.0, 1.0 27 | 28 | ] ); 29 | 30 | const pyramidVertexColor = new Float32Array( [ 31 | 32 | // Front face 33 | 1.0, 0.0, 0.0, 1.0, 34 | 0.0, 1.0, 0.0, 1.0, 35 | 0.0, 0.0, 1.0, 1.0, 36 | 37 | // Right face 38 | 1.0, 0.0, 0.0, 1.0, 39 | 0.0, 0.0, 1.0, 1.0, 40 | 0.0, 1.0, 0.0, 1.0, 41 | 42 | // Back face 43 | 1.0, 0.0, 0.0, 1.0, 44 | 0.0, 1.0, 0.0, 1.0, 45 | 0.0, 0.0, 1.0, 1.0, 46 | 47 | // Left face 48 | 1.0, 0.0, 0.0, 1.0, 49 | 0.0, 0.0, 1.0, 1.0, 50 | 0.0, 1.0, 0.0, 1.0 51 | 52 | ] ); 53 | 54 | const pyramidMVMatrix = new Matrix4(); 55 | 56 | const cubeVertexPosition = new Float32Array( [ 57 | 58 | // Front face 59 | -1.0, -1.0, 1.0, 60 | 1.0, -1.0, 1.0, 61 | 1.0, 1.0, 1.0, 62 | -1.0, 1.0, 1.0, 63 | 64 | // Back face 65 | -1.0, -1.0, -1.0, 66 | -1.0, 1.0, -1.0, 67 | 1.0, 1.0, -1.0, 68 | 1.0, -1.0, -1.0, 69 | 70 | // Top face 71 | -1.0, 1.0, -1.0, 72 | -1.0, 1.0, 1.0, 73 | 1.0, 1.0, 1.0, 74 | 1.0, 1.0, -1.0, 75 | 76 | // Bottom face 77 | -1.0, -1.0, -1.0, 78 | 1.0, -1.0, -1.0, 79 | 1.0, -1.0, 1.0, 80 | -1.0, -1.0, 1.0, 81 | 82 | // Right face 83 | 1.0, -1.0, -1.0, 84 | 1.0, 1.0, -1.0, 85 | 1.0, 1.0, 1.0, 86 | 1.0, -1.0, 1.0, 87 | 88 | // Left face 89 | -1.0, -1.0, -1.0, 90 | -1.0, -1.0, 1.0, 91 | -1.0, 1.0, 1.0, 92 | -1.0, 1.0, -1.0 93 | 94 | ] ); 95 | 96 | const cubeVertexColor = new Float32Array( [ 97 | 98 | 1.0, 0.0, 0.0, 1.0, // Front face 99 | 1.0, 0.0, 0.0, 1.0, // Front face 100 | 1.0, 0.0, 0.0, 1.0, // Front face 101 | 1.0, 0.0, 0.0, 1.0, // Front face 102 | 1.0, 1.0, 0.0, 1.0, // Back face 103 | 1.0, 1.0, 0.0, 1.0, // Back face 104 | 1.0, 1.0, 0.0, 1.0, // Back face 105 | 1.0, 1.0, 0.0, 1.0, // Back face 106 | 0.0, 1.0, 0.0, 1.0, // Top face 107 | 0.0, 1.0, 0.0, 1.0, // Top face 108 | 0.0, 1.0, 0.0, 1.0, // Top face 109 | 0.0, 1.0, 0.0, 1.0, // Top face 110 | 1.0, 0.5, 0.5, 1.0, // Bottom face 111 | 1.0, 0.5, 0.5, 1.0, // Bottom face 112 | 1.0, 0.5, 0.5, 1.0, // Bottom face 113 | 1.0, 0.5, 0.5, 1.0, // Bottom face 114 | 1.0, 0.0, 1.0, 1.0, // Right face 115 | 1.0, 0.0, 1.0, 1.0, // Right face 116 | 1.0, 0.0, 1.0, 1.0, // Right face 117 | 1.0, 0.0, 1.0, 1.0, // Right face 118 | 0.0, 0.0, 1.0, 1.0, // Left face 119 | 0.0, 0.0, 1.0, 1.0, // Left face 120 | 0.0, 0.0, 1.0, 1.0, // Left face 121 | 0.0, 0.0, 1.0, 1.0 // Left face 122 | 123 | ] ); 124 | 125 | const cubeIndex = new Uint32Array( [ 126 | 127 | 0, 1, 2, 0, 2, 3, // Front face 128 | 4, 5, 6, 4, 6, 7, // Back face 129 | 8, 9, 10, 8, 10, 11, // Top face 130 | 12, 13, 14, 12, 14, 15, // Bottom face 131 | 16, 17, 18, 16, 18, 19, // Right face 132 | 20, 21, 22, 20, 22, 23 // Left face 133 | 134 | ] ); 135 | 136 | const cubeMVMatrix = new Matrix4(); 137 | 138 | let main = async () => { 139 | 140 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 141 | 142 | let pMatrix = camera.projectionMatrix; 143 | 144 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 145 | 146 | let app = new App(); 147 | 148 | app.CreateCanvas( document.body ) 149 | 150 | .then( ( { width, height } ) => { 151 | 152 | return app.InitWebGPU( width, height ); 153 | 154 | }) 155 | 156 | .then( ( { colorAttachmentView, depthStencilAttachmentView } ) => { 157 | 158 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ) 159 | 160 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 161 | 162 | let lastTime = 0, rPyramid = 0, rCube = 0; 163 | 164 | let animate = () => { 165 | 166 | let timeNow = new Date().getTime(); 167 | 168 | if ( lastTime != 0 ) { 169 | 170 | let elapsed = timeNow - lastTime; 171 | 172 | rPyramid += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 173 | 174 | rCube -= ( Math.PI / 180 * 75 * elapsed ) / 1000.0; 175 | 176 | } 177 | 178 | lastTime = timeNow; 179 | } 180 | 181 | app.RunRenderLoop( () => { 182 | 183 | animate(); 184 | 185 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ); 186 | 187 | app.renderPassEncoder.setPipeline( app.renderPipeline ); 188 | 189 | pyramidMVMatrix.makeTranslation( -1.5, 0.0, -8.0 ).multiply( new Matrix4().makeRotationY( rPyramid ) ); 190 | 191 | cubeMVMatrix.makeTranslation( 1.5, 0.0, -8.0 ).multiply( new Matrix4().makeRotationAxis( new Vector3( 1.0, 1.0, 1.0 ).normalize(), rCube ) ); 192 | 193 | let pyramidUniformBufferView = new Float32Array( pMatrix.toArray().concat( pyramidMVMatrix.toArray() ) ); 194 | 195 | let cubeUniformBufferView = new Float32Array( pMatrix.toArray().concat( cubeMVMatrix.toArray() ) ); 196 | 197 | app.InitGPUBufferWithMultiBuffers( pyramidVertexPositon, pyramidVertexColor, pyramidUniformBufferView ); 198 | 199 | app.Draw( pyramidVertexPositon.length / 3 ); 200 | 201 | app.InitGPUBufferWithMultiBuffers( cubeVertexPosition, cubeVertexColor, cubeUniformBufferView, cubeIndex ); 202 | 203 | app.DrawIndexed( cubeIndex.length ); 204 | 205 | app.Present(); 206 | 207 | } ); 208 | 209 | } ); 210 | 211 | } 212 | 213 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@stage(fragment) 3 | fn main( 4 | @location(0) vColor: vec4 5 | ) -> @location(0) vec4 { 6 | return vColor; 7 | }`; -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vColor: vec4; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @location(0) aVertexPosition: vec3, 18 | @location(1) aVertexColor: vec4 19 | ) -> Output { 20 | var output: Output; 21 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 22 | output.vColor = aVertexColor; 23 | return output; 24 | }`; -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson4_Someting_real_3D/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson5_Texture/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson5_Texture/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson5_Texture/Code/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson5-texture", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "lesson5-texture", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@webgpu/glslang": "0.0.12", 12 | "@webgpu/types": "^0.1.13", 13 | "three": "^0.125.2" 14 | }, 15 | "devDependencies": { 16 | "vite": "^2.3.8" 17 | } 18 | }, 19 | "node_modules/@webgpu/glslang": { 20 | "version": "0.0.12", 21 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 22 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 23 | }, 24 | "node_modules/@webgpu/types": { 25 | "version": "0.1.13", 26 | "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.13.tgz", 27 | "integrity": "sha512-SAq8FRONvMANQi/eXw5ArKfSvih6am/EC+5y7+du2xf1VyprtKn4ylUPKGW4T6ZkDogtH3xZgGE+J/cx601L5w==" 28 | }, 29 | "node_modules/colorette": { 30 | "version": "1.2.2", 31 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 32 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 33 | "dev": true, 34 | "license": "MIT" 35 | }, 36 | "node_modules/esbuild": { 37 | "version": "0.12.9", 38 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 39 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 40 | "dev": true, 41 | "hasInstallScript": true, 42 | "license": "MIT", 43 | "bin": { 44 | "esbuild": "bin/esbuild" 45 | } 46 | }, 47 | "node_modules/fsevents": { 48 | "version": "2.3.2", 49 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 50 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 51 | "dev": true, 52 | "optional": true, 53 | "os": [ 54 | "darwin" 55 | ], 56 | "engines": { 57 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 58 | } 59 | }, 60 | "node_modules/function-bind": { 61 | "version": "1.1.1", 62 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 63 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 64 | "dev": true 65 | }, 66 | "node_modules/has": { 67 | "version": "1.0.3", 68 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 69 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 70 | "dev": true, 71 | "dependencies": { 72 | "function-bind": "^1.1.1" 73 | }, 74 | "engines": { 75 | "node": ">= 0.4.0" 76 | } 77 | }, 78 | "node_modules/is-core-module": { 79 | "version": "2.2.0", 80 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 81 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 82 | "dev": true, 83 | "dependencies": { 84 | "has": "^1.0.3" 85 | } 86 | }, 87 | "node_modules/nanoid": { 88 | "version": "3.3.0", 89 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 90 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 91 | "dev": true, 92 | "bin": { 93 | "nanoid": "bin/nanoid.cjs" 94 | }, 95 | "engines": { 96 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 97 | } 98 | }, 99 | "node_modules/path-parse": { 100 | "version": "1.0.7", 101 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 102 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 103 | "dev": true 104 | }, 105 | "node_modules/postcss": { 106 | "version": "8.3.5", 107 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 108 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 109 | "dev": true, 110 | "license": "MIT", 111 | "dependencies": { 112 | "colorette": "^1.2.2", 113 | "nanoid": "^3.1.23", 114 | "source-map-js": "^0.6.2" 115 | }, 116 | "engines": { 117 | "node": "^10 || ^12 || >=14" 118 | }, 119 | "funding": { 120 | "type": "opencollective", 121 | "url": "https://opencollective.com/postcss/" 122 | } 123 | }, 124 | "node_modules/resolve": { 125 | "version": "1.20.0", 126 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 127 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 128 | "dev": true, 129 | "dependencies": { 130 | "is-core-module": "^2.2.0", 131 | "path-parse": "^1.0.6" 132 | } 133 | }, 134 | "node_modules/rollup": { 135 | "version": "2.39.0", 136 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 137 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 138 | "dev": true, 139 | "bin": { 140 | "rollup": "dist/bin/rollup" 141 | }, 142 | "engines": { 143 | "node": ">=10.0.0" 144 | }, 145 | "optionalDependencies": { 146 | "fsevents": "~2.3.1" 147 | } 148 | }, 149 | "node_modules/source-map-js": { 150 | "version": "0.6.2", 151 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 152 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 153 | "dev": true, 154 | "license": "BSD-3-Clause", 155 | "engines": { 156 | "node": ">=0.10.0" 157 | } 158 | }, 159 | "node_modules/three": { 160 | "version": "0.125.2", 161 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 162 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 163 | }, 164 | "node_modules/vite": { 165 | "version": "2.3.8", 166 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 167 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 168 | "dev": true, 169 | "license": "MIT", 170 | "dependencies": { 171 | "esbuild": "^0.12.8", 172 | "postcss": "^8.3.4", 173 | "resolve": "^1.20.0", 174 | "rollup": "^2.38.5" 175 | }, 176 | "bin": { 177 | "vite": "bin/vite.js" 178 | }, 179 | "engines": { 180 | "node": ">=12.0.0" 181 | }, 182 | "optionalDependencies": { 183 | "fsevents": "~2.3.2" 184 | } 185 | } 186 | }, 187 | "dependencies": { 188 | "@webgpu/glslang": { 189 | "version": "0.0.12", 190 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 191 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 192 | }, 193 | "@webgpu/types": { 194 | "version": "0.1.13", 195 | "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.13.tgz", 196 | "integrity": "sha512-SAq8FRONvMANQi/eXw5ArKfSvih6am/EC+5y7+du2xf1VyprtKn4ylUPKGW4T6ZkDogtH3xZgGE+J/cx601L5w==" 197 | }, 198 | "colorette": { 199 | "version": "1.2.2", 200 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 201 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 202 | "dev": true 203 | }, 204 | "esbuild": { 205 | "version": "0.12.9", 206 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 207 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 208 | "dev": true 209 | }, 210 | "fsevents": { 211 | "version": "2.3.2", 212 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 213 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 214 | "dev": true, 215 | "optional": true 216 | }, 217 | "function-bind": { 218 | "version": "1.1.1", 219 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 220 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 221 | "dev": true 222 | }, 223 | "has": { 224 | "version": "1.0.3", 225 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 226 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 227 | "dev": true, 228 | "requires": { 229 | "function-bind": "^1.1.1" 230 | } 231 | }, 232 | "is-core-module": { 233 | "version": "2.2.0", 234 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 235 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 236 | "dev": true, 237 | "requires": { 238 | "has": "^1.0.3" 239 | } 240 | }, 241 | "nanoid": { 242 | "version": "3.3.0", 243 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 244 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 245 | "dev": true 246 | }, 247 | "path-parse": { 248 | "version": "1.0.7", 249 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 250 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 251 | "dev": true 252 | }, 253 | "postcss": { 254 | "version": "8.3.5", 255 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 256 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 257 | "dev": true, 258 | "requires": { 259 | "colorette": "^1.2.2", 260 | "nanoid": "^3.1.23", 261 | "source-map-js": "^0.6.2" 262 | } 263 | }, 264 | "resolve": { 265 | "version": "1.20.0", 266 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 267 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 268 | "dev": true, 269 | "requires": { 270 | "is-core-module": "^2.2.0", 271 | "path-parse": "^1.0.6" 272 | } 273 | }, 274 | "rollup": { 275 | "version": "2.39.0", 276 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 277 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 278 | "dev": true, 279 | "requires": { 280 | "fsevents": "~2.3.1" 281 | } 282 | }, 283 | "source-map-js": { 284 | "version": "0.6.2", 285 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 286 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 287 | "dev": true 288 | }, 289 | "three": { 290 | "version": "0.125.2", 291 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 292 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 293 | }, 294 | "vite": { 295 | "version": "2.3.8", 296 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 297 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 298 | "dev": true, 299 | "requires": { 300 | "esbuild": "^0.12.8", 301 | "fsevents": "~2.3.2", 302 | "postcss": "^8.3.4", 303 | "resolve": "^1.20.0", 304 | "rollup": "^2.38.5" 305 | } 306 | } 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /Lesson5_Texture/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson5-texture", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/image.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.gif'; -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 5 - Texture 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | import neheGIF from './texture/nehe.gif'; 6 | 7 | const cubeVertexPosition = new Float32Array( [ 8 | 9 | // Front face 10 | -1.0, -1.0, 1.0, 11 | 1.0, -1.0, 1.0, 12 | 1.0, 1.0, 1.0, 13 | -1.0, 1.0, 1.0, 14 | 15 | // Back face 16 | -1.0, -1.0, -1.0, 17 | -1.0, 1.0, -1.0, 18 | 1.0, 1.0, -1.0, 19 | 1.0, -1.0, -1.0, 20 | 21 | // Top face 22 | -1.0, 1.0, -1.0, 23 | -1.0, 1.0, 1.0, 24 | 1.0, 1.0, 1.0, 25 | 1.0, 1.0, -1.0, 26 | 27 | // Bottom face 28 | -1.0, -1.0, -1.0, 29 | 1.0, -1.0, -1.0, 30 | 1.0, -1.0, 1.0, 31 | -1.0, -1.0, 1.0, 32 | 33 | // Right face 34 | 1.0, -1.0, -1.0, 35 | 1.0, 1.0, -1.0, 36 | 1.0, 1.0, 1.0, 37 | 1.0, -1.0, 1.0, 38 | 39 | // Left face 40 | -1.0, -1.0, -1.0, 41 | -1.0, -1.0, 1.0, 42 | -1.0, 1.0, 1.0, 43 | -1.0, 1.0, -1.0, 44 | 45 | ] ); 46 | 47 | const cubeVertexUV = new Float32Array( [ 48 | 49 | // Front face 50 | 0.0, 0.0, 51 | 1.0, 0.0, 52 | 1.0, 1.0, 53 | 0.0, 1.0, 54 | 55 | // Back face 56 | 1.0, 0.0, 57 | 1.0, 1.0, 58 | 0.0, 1.0, 59 | 0.0, 0.0, 60 | 61 | // Top face 62 | 0.0, 1.0, 63 | 0.0, 0.0, 64 | 1.0, 0.0, 65 | 1.0, 1.0, 66 | 67 | // Bottom face 68 | 1.0, 1.0, 69 | 0.0, 1.0, 70 | 0.0, 0.0, 71 | 1.0, 0.0, 72 | 73 | // Right face 74 | 1.0, 0.0, 75 | 1.0, 1.0, 76 | 0.0, 1.0, 77 | 0.0, 0.0, 78 | 79 | // Left face 80 | 0.0, 0.0, 81 | 1.0, 0.0, 82 | 1.0, 1.0, 83 | 0.0, 1.0, 84 | 85 | ] ); 86 | 87 | const cubeIndex = new Uint32Array( [ 88 | 89 | 0, 1, 2, 0, 2, 3, // Front face 90 | 4, 5, 6, 4, 6, 7, // Back face 91 | 8, 9, 10, 8, 10, 11, // Top face 92 | 12, 13, 14, 12, 14, 15, // Bottom face 93 | 16, 17, 18, 16, 18, 19, // Right face 94 | 20, 21, 22, 20, 22, 23 // Left face 95 | 96 | ] ); 97 | 98 | const cubeMVMatrix = new Matrix4(); 99 | 100 | let main = async () => { 101 | 102 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 103 | 104 | let pMatrix = camera.projectionMatrix; 105 | 106 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 107 | 108 | let app = new App(); 109 | 110 | app.CreateCanvas( document.body ) 111 | 112 | .then( ( { width, height } ) => { 113 | 114 | return app.InitWebGPU( width, height ); 115 | 116 | }) 117 | 118 | .then( ( { colorAttachmentView, depthStencilAttachmentView } ) => { 119 | 120 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ) 121 | 122 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 123 | 124 | return app.LoadTexture( neheGIF ) 125 | 126 | .then( ( { texture, sampler } ) => { 127 | 128 | return { texture, sampler, colorAttachmentView, depthStencilAttachmentView }; 129 | 130 | } ); 131 | 132 | } ) 133 | 134 | .then( ( { texture, sampler, colorAttachmentView, depthStencilAttachmentView } ) => { 135 | 136 | let lastTime = 0, xRot = 0, yRot = 0, zRot = 0; 137 | 138 | let animate = () => { 139 | 140 | let timeNow = new Date().getTime(); 141 | 142 | if ( lastTime != 0 ) { 143 | 144 | let elapsed = timeNow - lastTime; 145 | 146 | xRot += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 147 | yRot += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 148 | zRot += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 149 | } 150 | 151 | lastTime = timeNow; 152 | } 153 | 154 | app.RunRenderLoop( () => { 155 | 156 | animate(); 157 | 158 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ); 159 | 160 | app.renderPassEncoder.setPipeline( app.renderPipeline ); 161 | 162 | cubeMVMatrix.makeTranslation( 0, 0, -5.0 ) 163 | .multiply( new Matrix4().makeRotationX( xRot ) ) 164 | .multiply( new Matrix4().makeRotationY( yRot ) ) 165 | .multiply( new Matrix4().makeRotationZ( zRot ) ); 166 | 167 | let cubeUniformBufferView = new Float32Array( pMatrix.toArray().concat( cubeMVMatrix.toArray() ) ); 168 | 169 | app.InitGPUBufferWithMultiBuffers( cubeVertexPosition, cubeVertexUV, cubeUniformBufferView, cubeIndex, texture, sampler ); 170 | 171 | app.DrawIndexed( cubeIndex.length ); 172 | 173 | app.Present(); 174 | 175 | } ); 176 | 177 | }) 178 | 179 | } 180 | 181 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@group(0) @binding(1) 3 | var uSampler: sampler; 4 | 5 | @group(0) @binding(2) 6 | var cubeTexture: texture_2d; 7 | 8 | @stage(fragment) 9 | fn main( 10 | @location(0) vUV: vec2 11 | ) -> @location(0) vec4 { 12 | return textureSample(cubeTexture, uSampler, vec2(vUV.x, 1.0 - vUV.y)); 13 | } 14 | ` -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vUV: vec2; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @location(0) aVertexPosition: vec3, 18 | @location(1) aVertexUV: vec2 19 | ) -> Output { 20 | var output: Output; 21 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 22 | output.vUV = aVertexUV; 23 | return output; 24 | }`; -------------------------------------------------------------------------------- /Lesson5_Texture/Code/src/texture/nehe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson5_Texture/Code/src/texture/nehe.gif -------------------------------------------------------------------------------- /Lesson5_Texture/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson5_Texture/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson6-interactive-and-texture-filter", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.13", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/image.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.gif'; -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 6 - Interactive and texture filter 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | import crateGIF from './texture/crate.gif'; 6 | 7 | const cubeVertexPosition = new Float32Array( [ 8 | 9 | // Front face 10 | -1.0, -1.0, 1.0, 11 | 1.0, -1.0, 1.0, 12 | 1.0, 1.0, 1.0, 13 | -1.0, 1.0, 1.0, 14 | 15 | // Back face 16 | -1.0, -1.0, -1.0, 17 | -1.0, 1.0, -1.0, 18 | 1.0, 1.0, -1.0, 19 | 1.0, -1.0, -1.0, 20 | 21 | // Top face 22 | -1.0, 1.0, -1.0, 23 | -1.0, 1.0, 1.0, 24 | 1.0, 1.0, 1.0, 25 | 1.0, 1.0, -1.0, 26 | 27 | // Bottom face 28 | -1.0, -1.0, -1.0, 29 | 1.0, -1.0, -1.0, 30 | 1.0, -1.0, 1.0, 31 | -1.0, -1.0, 1.0, 32 | 33 | // Right face 34 | 1.0, -1.0, -1.0, 35 | 1.0, 1.0, -1.0, 36 | 1.0, 1.0, 1.0, 37 | 1.0, -1.0, 1.0, 38 | 39 | // Left face 40 | -1.0, -1.0, -1.0, 41 | -1.0, -1.0, 1.0, 42 | -1.0, 1.0, 1.0, 43 | -1.0, 1.0, -1.0, 44 | 45 | ] ); 46 | 47 | const cubeVertexUV = new Float32Array( [ 48 | 49 | // Front face 50 | 0.0, 0.0, 51 | 1.0, 0.0, 52 | 1.0, 1.0, 53 | 0.0, 1.0, 54 | 55 | // Back face 56 | 1.0, 0.0, 57 | 1.0, 1.0, 58 | 0.0, 1.0, 59 | 0.0, 0.0, 60 | 61 | // Top face 62 | 0.0, 1.0, 63 | 0.0, 0.0, 64 | 1.0, 0.0, 65 | 1.0, 1.0, 66 | 67 | // Bottom face 68 | 1.0, 1.0, 69 | 0.0, 1.0, 70 | 0.0, 0.0, 71 | 1.0, 0.0, 72 | 73 | // Right face 74 | 1.0, 0.0, 75 | 1.0, 1.0, 76 | 0.0, 1.0, 77 | 0.0, 0.0, 78 | 79 | // Left face 80 | 0.0, 0.0, 81 | 1.0, 0.0, 82 | 1.0, 1.0, 83 | 0.0, 1.0, 84 | 85 | ] ); 86 | 87 | const cubeIndex = new Uint32Array( [ 88 | 89 | 0, 1, 2, 0, 2, 3, // Front face 90 | 4, 5, 6, 4, 6, 7, // Back face 91 | 8, 9, 10, 8, 10, 11, // Top face 92 | 12, 13, 14, 12, 14, 15, // Bottom face 93 | 16, 17, 18, 16, 18, 19, // Right face 94 | 20, 21, 22, 20, 22, 23 // Left face 95 | 96 | ] ); 97 | 98 | const cubeMVMatrix = new Matrix4(); 99 | 100 | let main = async () => { 101 | 102 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 103 | 104 | let pMatrix = camera.projectionMatrix; 105 | 106 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 107 | 108 | let app = new App(); 109 | 110 | app.CreateCanvas( document.body ) 111 | 112 | .then( ( { width, height } ) => { 113 | 114 | return app.InitWebGPU( width, height ); 115 | 116 | }) 117 | 118 | .then( ( { colorAttachmentView, depthStencilAttachmentView } ) => { 119 | 120 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ) 121 | 122 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 123 | 124 | return app.LoadTexture( crateGIF ) 125 | 126 | .then( ( texture ) => { 127 | 128 | return { texture, colorAttachmentView, depthStencilAttachmentView }; 129 | 130 | } ); 131 | 132 | } ) 133 | 134 | .then( ( { texture, colorAttachmentView, depthStencilAttachmentView } ) => { 135 | 136 | let lastTime = 0, z = -5.0, xSpeed = 0, ySpeed = 0, xRot = 0, yRot = 0, samplerIndex = 0; 137 | 138 | app.AddKeyboardEventListener( 'keyup', 'f', () => { 139 | 140 | if ( samplerIndex === 2 ) { 141 | 142 | samplerIndex = 0 143 | 144 | } else { 145 | 146 | samplerIndex ++ 147 | 148 | } 149 | 150 | } ); 151 | 152 | app.AddKeyboardEventListener( 'keydown', 'PageUp', () => z += 0.05 ); 153 | 154 | app.AddKeyboardEventListener( 'keydown', 'PageDown', () => z -= 0.05 ); 155 | 156 | app.AddKeyboardEventListener( 'keydown', 'ArrowUp', () => xSpeed -= 1 ); 157 | 158 | app.AddKeyboardEventListener( 'keydown', 'ArrowDown', () => xSpeed += 1 ); 159 | 160 | app.AddKeyboardEventListener( 'keydown', 'ArrowLeft', () => ySpeed -= 1 ); 161 | 162 | app.AddKeyboardEventListener( 'keydown', 'ArrowRight', () => ySpeed += 1 ); 163 | 164 | app.AddKeyboardEventListener( 'keyup', 'r', () => { 165 | 166 | lastTime = 0; 167 | z = -5.0; 168 | xSpeed = 0; 169 | ySpeed = 0; 170 | xRot = 0; 171 | yRot = 0; 172 | samplerIndex = 0; 173 | 174 | } ); 175 | 176 | let animate = () => { 177 | 178 | let timeNow = new Date().getTime(); 179 | 180 | if ( lastTime != 0 ) { 181 | 182 | let elapsed = timeNow - lastTime; 183 | 184 | xRot += ( Math.PI / 180 * xSpeed * elapsed ) / 1000.0; 185 | yRot += ( Math.PI / 180 * ySpeed * elapsed ) / 1000.0; 186 | } 187 | 188 | lastTime = timeNow; 189 | 190 | } 191 | 192 | app.RunRenderLoop( () => { 193 | 194 | animate(); 195 | 196 | app.InitRenderPass( backgroundColor, colorAttachmentView, depthStencilAttachmentView ); 197 | 198 | app.renderPassEncoder.setPipeline( app.renderPipeline ); 199 | 200 | cubeMVMatrix.makeTranslation( 0, 0, z ) 201 | .multiply( new Matrix4().makeRotationX( xRot ) ) 202 | .multiply( new Matrix4().makeRotationY( yRot ) ); 203 | 204 | let cubeUniformBufferView = new Float32Array( pMatrix.toArray().concat( cubeMVMatrix.toArray() ) ); 205 | 206 | app.InitGPUBufferWithMultiBuffers( cubeVertexPosition, cubeVertexUV, cubeUniformBufferView, cubeIndex, texture, app.samplers[ samplerIndex ] ); 207 | 208 | app.DrawIndexed( cubeIndex.length ); 209 | 210 | app.Present(); 211 | 212 | } ); 213 | 214 | }) 215 | 216 | } 217 | 218 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@group(0) @binding(1) 3 | var uSampler: sampler; 4 | 5 | @group(0) @binding(2) 6 | var cubeTexture: texture_2d; 7 | 8 | @stage(fragment) 9 | fn main( 10 | @location(0) vUV: vec2 11 | ) -> @location(0) vec4 { 12 | return textureSample(cubeTexture, uSampler, vec2(vUV.x, 1.0 - vUV.y)); 13 | } 14 | ` -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vUV: vec2; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @location(0) aVertexPosition: vec3, 18 | @location(1) aVertexUV: vec2 19 | ) -> Output { 20 | var output: Output; 21 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * vec4(aVertexPosition, 1.0); 22 | output.vUV = aVertexUV; 23 | return output; 24 | }`; -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/src/texture/crate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson6_Interactive_and_texture_filter/Code/src/texture/crate.gif -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson6_Interactive_and_texture_filter/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson7-directional-light-and-ambient-light", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "lesson7-directional-light-and-ambient-light", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@webgpu/glslang": "0.0.12", 12 | "@webgpu/types": "0.0.45", 13 | "three": "^0.125.2" 14 | }, 15 | "devDependencies": { 16 | "vite": "^2.3.8" 17 | } 18 | }, 19 | "node_modules/@webgpu/glslang": { 20 | "version": "0.0.12", 21 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 22 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 23 | }, 24 | "node_modules/@webgpu/types": { 25 | "version": "0.0.45", 26 | "resolved": "https://registry.npm.taobao.org/@webgpu/types/download/@webgpu/types-0.0.45.tgz", 27 | "integrity": "sha1-zvmX/36gTthGL1yHSzzvBCRoGT0=" 28 | }, 29 | "node_modules/colorette": { 30 | "version": "1.2.2", 31 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 32 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 33 | "dev": true, 34 | "license": "MIT" 35 | }, 36 | "node_modules/esbuild": { 37 | "version": "0.12.9", 38 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 39 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 40 | "dev": true, 41 | "hasInstallScript": true, 42 | "license": "MIT", 43 | "bin": { 44 | "esbuild": "bin/esbuild" 45 | } 46 | }, 47 | "node_modules/fsevents": { 48 | "version": "2.3.2", 49 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 50 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 51 | "dev": true, 52 | "optional": true, 53 | "os": [ 54 | "darwin" 55 | ], 56 | "engines": { 57 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 58 | } 59 | }, 60 | "node_modules/function-bind": { 61 | "version": "1.1.1", 62 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 63 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 64 | "dev": true 65 | }, 66 | "node_modules/has": { 67 | "version": "1.0.3", 68 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 69 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 70 | "dev": true, 71 | "dependencies": { 72 | "function-bind": "^1.1.1" 73 | }, 74 | "engines": { 75 | "node": ">= 0.4.0" 76 | } 77 | }, 78 | "node_modules/is-core-module": { 79 | "version": "2.2.0", 80 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 81 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 82 | "dev": true, 83 | "dependencies": { 84 | "has": "^1.0.3" 85 | } 86 | }, 87 | "node_modules/nanoid": { 88 | "version": "3.3.0", 89 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 90 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 91 | "dev": true, 92 | "bin": { 93 | "nanoid": "bin/nanoid.cjs" 94 | }, 95 | "engines": { 96 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 97 | } 98 | }, 99 | "node_modules/path-parse": { 100 | "version": "1.0.7", 101 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 102 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 103 | "dev": true 104 | }, 105 | "node_modules/postcss": { 106 | "version": "8.3.5", 107 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 108 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 109 | "dev": true, 110 | "license": "MIT", 111 | "dependencies": { 112 | "colorette": "^1.2.2", 113 | "nanoid": "^3.1.23", 114 | "source-map-js": "^0.6.2" 115 | }, 116 | "engines": { 117 | "node": "^10 || ^12 || >=14" 118 | }, 119 | "funding": { 120 | "type": "opencollective", 121 | "url": "https://opencollective.com/postcss/" 122 | } 123 | }, 124 | "node_modules/rollup": { 125 | "version": "2.39.0", 126 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 127 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 128 | "dev": true, 129 | "bin": { 130 | "rollup": "dist/bin/rollup" 131 | }, 132 | "engines": { 133 | "node": ">=10.0.0" 134 | }, 135 | "optionalDependencies": { 136 | "fsevents": "~2.3.1" 137 | } 138 | }, 139 | "node_modules/source-map-js": { 140 | "version": "0.6.2", 141 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 142 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 143 | "dev": true, 144 | "license": "BSD-3-Clause", 145 | "engines": { 146 | "node": ">=0.10.0" 147 | } 148 | }, 149 | "node_modules/three": { 150 | "version": "0.125.2", 151 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 152 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 153 | }, 154 | "node_modules/vite": { 155 | "version": "2.3.8", 156 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 157 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 158 | "dev": true, 159 | "license": "MIT", 160 | "dependencies": { 161 | "esbuild": "^0.12.8", 162 | "postcss": "^8.3.4", 163 | "resolve": "^1.20.0", 164 | "rollup": "^2.38.5" 165 | }, 166 | "bin": { 167 | "vite": "bin/vite.js" 168 | }, 169 | "engines": { 170 | "node": ">=12.0.0" 171 | }, 172 | "optionalDependencies": { 173 | "fsevents": "~2.3.2" 174 | } 175 | }, 176 | "node_modules/vite/node_modules/resolve": { 177 | "version": "1.20.0", 178 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 179 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 180 | "dev": true, 181 | "dependencies": { 182 | "is-core-module": "^2.2.0", 183 | "path-parse": "^1.0.6" 184 | } 185 | } 186 | }, 187 | "dependencies": { 188 | "@webgpu/glslang": { 189 | "version": "0.0.12", 190 | "resolved": "http://registry.npmjs.lianjia.com:7001/@webgpu/glslang/download/@webgpu/glslang-0.0.12.tgz", 191 | "integrity": "sha1-7kDo04wxQ2UIFH/q8PZG/em7TaY=" 192 | }, 193 | "@webgpu/types": { 194 | "version": "0.0.45", 195 | "resolved": "https://registry.npm.taobao.org/@webgpu/types/download/@webgpu/types-0.0.45.tgz", 196 | "integrity": "sha1-zvmX/36gTthGL1yHSzzvBCRoGT0=" 197 | }, 198 | "colorette": { 199 | "version": "1.2.2", 200 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/colorette/download/colorette-1.2.2.tgz", 201 | "integrity": "sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=", 202 | "dev": true 203 | }, 204 | "esbuild": { 205 | "version": "0.12.9", 206 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/esbuild/download/esbuild-0.12.9.tgz", 207 | "integrity": "sha1-vtTnCHwobNgdl1Yx931H/rFmAHA=", 208 | "dev": true 209 | }, 210 | "fsevents": { 211 | "version": "2.3.2", 212 | "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536422255&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", 213 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 214 | "dev": true, 215 | "optional": true 216 | }, 217 | "function-bind": { 218 | "version": "1.1.1", 219 | "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", 220 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 221 | "dev": true 222 | }, 223 | "has": { 224 | "version": "1.0.3", 225 | "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", 226 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 227 | "dev": true, 228 | "requires": { 229 | "function-bind": "^1.1.1" 230 | } 231 | }, 232 | "is-core-module": { 233 | "version": "2.2.0", 234 | "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411621990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", 235 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 236 | "dev": true, 237 | "requires": { 238 | "has": "^1.0.3" 239 | } 240 | }, 241 | "nanoid": { 242 | "version": "3.3.0", 243 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", 244 | "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", 245 | "dev": true 246 | }, 247 | "path-parse": { 248 | "version": "1.0.7", 249 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 250 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 251 | "dev": true 252 | }, 253 | "postcss": { 254 | "version": "8.3.5", 255 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/postcss/download/postcss-8.3.5.tgz", 256 | "integrity": "sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=", 257 | "dev": true, 258 | "requires": { 259 | "colorette": "^1.2.2", 260 | "nanoid": "^3.1.23", 261 | "source-map-js": "^0.6.2" 262 | } 263 | }, 264 | "rollup": { 265 | "version": "2.39.0", 266 | "resolved": "https://registry.npm.taobao.org/rollup/download/rollup-2.39.0.tgz?cache=0&sync_timestamp=1613145837486&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frollup%2Fdownload%2Frollup-2.39.0.tgz", 267 | "integrity": "sha1-vk+YyeQheTqP7ILIVPtWfDXiKrY=", 268 | "dev": true, 269 | "requires": { 270 | "fsevents": "~2.3.1" 271 | } 272 | }, 273 | "source-map-js": { 274 | "version": "0.6.2", 275 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/source-map-js/download/source-map-js-0.6.2.tgz", 276 | "integrity": "sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=", 277 | "dev": true 278 | }, 279 | "three": { 280 | "version": "0.125.2", 281 | "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", 282 | "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" 283 | }, 284 | "vite": { 285 | "version": "2.3.8", 286 | "resolved": "http://artifactory.intra.ke.com/artifactory/api/npm/npm-virtual/vite/download/vite-2.3.8.tgz", 287 | "integrity": "sha1-QuPgOVOFn9QQ5OarPRzKCqsq3Dw=", 288 | "dev": true, 289 | "requires": { 290 | "esbuild": "^0.12.8", 291 | "fsevents": "~2.3.2", 292 | "postcss": "^8.3.4", 293 | "resolve": "^1.20.0", 294 | "rollup": "^2.38.5" 295 | }, 296 | "dependencies": { 297 | "resolve": { 298 | "version": "1.20.0", 299 | "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", 300 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 301 | "dev": true, 302 | "requires": { 303 | "is-core-module": "^2.2.0", 304 | "path-parse": "^1.0.6" 305 | } 306 | } 307 | } 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson7-directional-light-and-ambient-light", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "0.0.45", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/image.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.gif'; -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson 7 - Directional light and ambient light 8 | 40 | 41 | 42 |
43 |
44 |
45 |

操作说明

46 |

PageUPPageDown 键进行缩放

47 |

按方向键进行旋转

48 |
49 |
50 |

光照控制

51 | 启用光照 52 |
53 |
54 |

平行光

55 |

方向

56 |
57 | X: 58 | Y: 59 | Z: 60 |
61 |

颜色

62 |
63 | R: 64 | G: 65 | B: 66 |
67 |
68 |
69 |

环境光

70 |

颜色

71 |
72 | R: 73 | G: 74 | B: 75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.glsl'; 3 | import fxCode from './shader/fragment.glsl' 4 | import { PerspectiveCamera, Matrix4, Vector3, Color } from 'three'; 5 | import crateGIF from './texture/crate.gif'; 6 | 7 | const cubeVertexPosition = new Float32Array( [ 8 | 9 | // Front face 10 | -1.0, -1.0, 1.0, 11 | 1.0, -1.0, 1.0, 12 | 1.0, 1.0, 1.0, 13 | -1.0, 1.0, 1.0, 14 | 15 | // Back face 16 | -1.0, -1.0, -1.0, 17 | -1.0, 1.0, -1.0, 18 | 1.0, 1.0, -1.0, 19 | 1.0, -1.0, -1.0, 20 | 21 | // Top face 22 | -1.0, 1.0, -1.0, 23 | -1.0, 1.0, 1.0, 24 | 1.0, 1.0, 1.0, 25 | 1.0, 1.0, -1.0, 26 | 27 | // Bottom face 28 | -1.0, -1.0, -1.0, 29 | 1.0, -1.0, -1.0, 30 | 1.0, -1.0, 1.0, 31 | -1.0, -1.0, 1.0, 32 | 33 | // Right face 34 | 1.0, -1.0, -1.0, 35 | 1.0, 1.0, -1.0, 36 | 1.0, 1.0, 1.0, 37 | 1.0, -1.0, 1.0, 38 | 39 | // Left face 40 | -1.0, -1.0, -1.0, 41 | -1.0, -1.0, 1.0, 42 | -1.0, 1.0, 1.0, 43 | -1.0, 1.0, -1.0, 44 | 45 | ] ); 46 | 47 | const cubeVertexUV = new Float32Array( [ 48 | 49 | // Front face 50 | 0.0, 0.0, 51 | 1.0, 0.0, 52 | 1.0, 1.0, 53 | 0.0, 1.0, 54 | 55 | // Back face 56 | 1.0, 0.0, 57 | 1.0, 1.0, 58 | 0.0, 1.0, 59 | 0.0, 0.0, 60 | 61 | // Top face 62 | 0.0, 1.0, 63 | 0.0, 0.0, 64 | 1.0, 0.0, 65 | 1.0, 1.0, 66 | 67 | // Bottom face 68 | 1.0, 1.0, 69 | 0.0, 1.0, 70 | 0.0, 0.0, 71 | 1.0, 0.0, 72 | 73 | // Right face 74 | 1.0, 0.0, 75 | 1.0, 1.0, 76 | 0.0, 1.0, 77 | 0.0, 0.0, 78 | 79 | // Left face 80 | 0.0, 0.0, 81 | 1.0, 0.0, 82 | 1.0, 1.0, 83 | 0.0, 1.0, 84 | 85 | ] ); 86 | 87 | const cubeIndex = new Uint32Array( [ 88 | 89 | 0, 1, 2, 0, 2, 3, // Front face 90 | 4, 5, 6, 4, 6, 7, // Back face 91 | 8, 9, 10, 8, 10, 11, // Top face 92 | 12, 13, 14, 12, 14, 15, // Bottom face 93 | 16, 17, 18, 16, 18, 19, // Right face 94 | 20, 21, 22, 20, 22, 23 // Left face 95 | 96 | ] ); 97 | 98 | const cubeMVMatrix = new Matrix4(); 99 | 100 | const cubeVertexNormals = new Float32Array( [ 101 | // Front face 102 | 0.0, 0.0, 1.0, 103 | 0.0, 0.0, 1.0, 104 | 0.0, 0.0, 1.0, 105 | 0.0, 0.0, 1.0, 106 | 107 | // Back face 108 | 0.0, 0.0, -1.0, 109 | 0.0, 0.0, -1.0, 110 | 0.0, 0.0, -1.0, 111 | 0.0, 0.0, -1.0, 112 | 113 | // Top face 114 | 0.0, 1.0, 0.0, 115 | 0.0, 1.0, 0.0, 116 | 0.0, 1.0, 0.0, 117 | 0.0, 1.0, 0.0, 118 | 119 | // Bottom face 120 | 0.0, -1.0, 0.0, 121 | 0.0, -1.0, 0.0, 122 | 0.0, -1.0, 0.0, 123 | 0.0, -1.0, 0.0, 124 | 125 | // Right face 126 | 1.0, 0.0, 0.0, 127 | 1.0, 0.0, 0.0, 128 | 1.0, 0.0, 0.0, 129 | 1.0, 0.0, 0.0, 130 | 131 | // Left face 132 | -1.0, 0.0, 0.0, 133 | -1.0, 0.0, 0.0, 134 | -1.0, 0.0, 0.0, 135 | -1.0, 0.0, 0.0 136 | ] ); 137 | 138 | 139 | let main = async () => { 140 | 141 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 142 | 143 | let app = new App(); 144 | 145 | let canvasLayer = document.getElementById( 'canvasLayer' ); 146 | 147 | let camera = new PerspectiveCamera( 45, canvasLayer.clientWidth / canvasLayer.clientHeight, 0.1, 100 ); 148 | 149 | let pMatrix = camera.projectionMatrix; 150 | 151 | app.CreateCanvas( canvasLayer ) 152 | 153 | .then( ( { width, height } ) => { 154 | 155 | return app.InitWebGPU( width, height ); 156 | 157 | }) 158 | 159 | .then( ( { colorAttachment, depthStencilAttachment } ) => { 160 | 161 | app.InitRenderPass( backgroundColor, colorAttachment, depthStencilAttachment ) 162 | 163 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 164 | 165 | return app.LoadTexture( crateGIF ) 166 | 167 | .then( ( texture ) => { 168 | 169 | return { texture, colorAttachment, depthStencilAttachment }; 170 | 171 | } ); 172 | 173 | } ) 174 | 175 | .then( ( { texture, colorAttachment, depthStencilAttachment } ) => { 176 | 177 | let lastTime = 0, z = -5.0, xSpeed = 0, ySpeed = 0, xRot = 0, yRot = 0, samplerIndex = 0; 178 | 179 | app.AddKeyboardEventListener( 'keyup', 'f', () => { 180 | 181 | if ( samplerIndex === 2 ) { 182 | 183 | samplerIndex = 0 184 | 185 | } else { 186 | 187 | samplerIndex ++ 188 | 189 | } 190 | 191 | } ); 192 | 193 | app.AddKeyboardEventListener( 'keydown', 'PageUp', () => z += 0.05 ); 194 | 195 | app.AddKeyboardEventListener( 'keydown', 'PageDown', () => z -= 0.05 ); 196 | 197 | app.AddKeyboardEventListener( 'keydown', 'ArrowUp', () => xSpeed -= 1 ); 198 | 199 | app.AddKeyboardEventListener( 'keydown', 'ArrowDown', () => xSpeed += 1 ); 200 | 201 | app.AddKeyboardEventListener( 'keydown', 'ArrowLeft', () => ySpeed -= 1 ); 202 | 203 | app.AddKeyboardEventListener( 'keydown', 'ArrowRight', () => ySpeed += 1 ); 204 | 205 | app.AddKeyboardEventListener( 'keyup', 'r', () => { 206 | 207 | lastTime = 0; 208 | z = -5.0; 209 | xSpeed = 0; 210 | ySpeed = 0; 211 | xRot = 0; 212 | yRot = 0; 213 | samplerIndex = 0; 214 | 215 | } ); 216 | 217 | let animate = () => { 218 | 219 | let timeNow = new Date().getTime(); 220 | 221 | if ( lastTime != 0 ) { 222 | 223 | let elapsed = timeNow - lastTime; 224 | 225 | xRot += ( Math.PI / 180 * xSpeed * elapsed ) / 1000.0; 226 | yRot += ( Math.PI / 180 * ySpeed * elapsed ) / 1000.0; 227 | } 228 | 229 | lastTime = timeNow; 230 | 231 | } 232 | 233 | let makePositionUniformArray = ( ifUseLighting: boolean, mvMatrix: Matrix4, ambientColor: Color, uLightingDirection: Vector3, directionalColor: Color ) => { 234 | 235 | let clone = mvMatrix.clone(); 236 | 237 | clone.getInverse( clone ).transpose(); 238 | 239 | let result = new Float32Array( [ Number( ifUseLighting ), ...clone.toArray(), ...ambientColor.toArray(), ...uLightingDirection.normalize().toArray(), ...directionalColor.toArray() ] ); 240 | 241 | return result; 242 | 243 | } 244 | 245 | app.RunRenderLoop( () => { 246 | 247 | animate(); 248 | 249 | app.InitRenderPass( backgroundColor, colorAttachment, depthStencilAttachment ); 250 | 251 | app.renderPassEncoder.setPipeline( app.renderPipeline ); 252 | 253 | cubeMVMatrix.makeTranslation( 0, 0, z ) 254 | .multiply( new Matrix4().makeRotationX( xRot ) ) 255 | .multiply( new Matrix4().makeRotationY( yRot ) ); 256 | 257 | let positionUniformArray = new Float32Array( pMatrix.toArray().concat( cubeMVMatrix.toArray() ) ); 258 | 259 | let lightUniformArray = makePositionUniformArray( true, cubeMVMatrix, new Color( 0xffffff ), new Vector3( 0, -1, 0 ), new Color( 0xff0000 ) ); 260 | 261 | app.InitGPUBufferWithMultiBuffers( cubeVertexPosition, cubeVertexUV, cubeVertexNormals, positionUniformArray, lightUniformArray, cubeIndex, texture, app.samplers[ samplerIndex ] ); 262 | 263 | app.DrawIndexed( cubeIndex.length ); 264 | 265 | app.Present(); 266 | 267 | } ); 268 | 269 | }) 270 | 271 | } 272 | 273 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/shader/fragment.glsl.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | #version 450 3 | 4 | layout(set = 0, binding = 2) uniform sampler uSampler; 5 | layout(set = 0, binding = 3) uniform texture2D cubeTexture; 6 | 7 | layout(location = 0) in vec2 vUV; 8 | layout(location = 1) in vec3 vLightWeighting; 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main(void) { 12 | vec4 textureColor = texture(sampler2D(cubeTexture, uSampler), vec2(vUV.s, 1 - vUV.t) ); 13 | outColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a); 14 | } 15 | `; -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/shader/vertex.glsl.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | #version 450 3 | 4 | layout(binding = 0) uniform Position { 5 | 6 | mat4 uPMatrix; 7 | mat4 uMVMatrix; 8 | 9 | }; 10 | 11 | layout(binding = 1) uniform Light { 12 | 13 | bool useLight; 14 | mat4 uNMatrix; 15 | 16 | vec3 uAmbientColor; 17 | vec3 uLightingDirection; 18 | vec3 uDirectionalColor; 19 | 20 | }; 21 | 22 | layout(location = 0) in vec3 aVertexPosition; 23 | layout(location = 1) in vec2 aVertexUV; 24 | layout(location = 2) in vec3 aVertexNormal; 25 | layout(location = 0) out vec2 vUV; 26 | layout(location = 1) out vec3 vLightWeighting; 27 | 28 | void main() { 29 | 30 | 31 | vUV = aVertexUV; 32 | 33 | if (!useLight) { 34 | vLightWeighting = vec3(1.0, 1.0, 1.0); 35 | } else { 36 | vec3 transformedNormal = ( uNMatrix * vec4(aVertexNormal, 1.0)).xyz; 37 | float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0); 38 | vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting; 39 | } 40 | gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); 41 | 42 | } 43 | `; -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/src/texture/crate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjlld/LearningWebGPU/43fc14a638a24f49a25fc358f7e3dd2d06dc7edc/Lesson7_Directional_light_and_ambient_light/Code/src/texture/crate.gif -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Lesson7_Directional_light_and_ambient_light/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-syntax-import-meta"], 3 | "presets": ["@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | /.cache 4 | .parcel-cache -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lessonX-renderbundle", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.3.8" 11 | }, 12 | "dependencies": { 13 | "@webgpu/glslang": "0.0.12", 14 | "@webgpu/types": "^0.1.6", 15 | "three": "^0.125.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/src/app.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from 'three'; 2 | 3 | export class App { 4 | 5 | public canvas: HTMLCanvasElement; 6 | 7 | public adapter: GPUAdapter; 8 | 9 | public device: GPUDevice; 10 | 11 | public context: GPUCanvasContext; 12 | 13 | public format: GPUTextureFormat = 'bgra8unorm'; 14 | 15 | public commandEncoder: GPUCommandEncoder; 16 | 17 | public renderBundleEncoder: GPURenderBundleEncoder; 18 | 19 | public renderPassEncoder: GPURenderPassEncoder; 20 | 21 | public uniformGroupLayout: GPUBindGroupLayout; 22 | 23 | public renderPipeline: GPURenderPipeline; 24 | 25 | public devicePixelWidth: number; 26 | 27 | public devicePixelHeight: number; 28 | 29 | private _clearColor: GPUColorDict; 30 | 31 | public CreateCanvas( rootElement: HTMLElement ) { 32 | 33 | let width = rootElement.clientWidth; 34 | 35 | let height = rootElement.clientHeight; 36 | 37 | this.devicePixelWidth = width * window.devicePixelRatio; 38 | 39 | this.devicePixelHeight = height * window.devicePixelRatio; 40 | 41 | this.canvas = document.createElement( 'canvas' ); 42 | 43 | this.canvas.width = this.devicePixelWidth; 44 | 45 | this.canvas.height = this.devicePixelHeight; 46 | 47 | this.canvas.style.width = '100%'; 48 | 49 | this.canvas.style.height = '100%'; 50 | 51 | rootElement.appendChild( this.canvas ); 52 | 53 | } 54 | 55 | public async InitWebGPU() { 56 | 57 | this.adapter = await navigator.gpu.requestAdapter( { 58 | 59 | powerPreference: 'high-performance' 60 | 61 | } ); 62 | 63 | this.device = await this.adapter.requestDevice(); 64 | 65 | this.context = this.canvas.getContext( 'webgpu' ) as GPUCanvasContext; 66 | 67 | this.format = this.context.getPreferredFormat( this.adapter ); 68 | 69 | this.context.configure( { 70 | 71 | device: this.device, 72 | 73 | format: this.format, 74 | 75 | usage: GPUTextureUsage.RENDER_ATTACHMENT 76 | 77 | } ); 78 | 79 | } 80 | 81 | public InitRenderPass() { 82 | 83 | 84 | let renderBundleEncoderDescriptor: GPURenderBundleEncoderDescriptor = { 85 | 86 | colorFormats: [ this.format ] 87 | 88 | }; 89 | 90 | this.renderBundleEncoder = this.device.createRenderBundleEncoder( renderBundleEncoderDescriptor ); 91 | 92 | } 93 | 94 | public InitPipelineWitMultiBuffers( vxCode: string, fxCode: string ) { 95 | 96 | this.uniformGroupLayout = this.device.createBindGroupLayout( { 97 | 98 | entries: [ 99 | 100 | { 101 | 102 | binding: 0, 103 | 104 | visibility: GPUShaderStage.VERTEX, 105 | 106 | buffer: { 107 | 108 | type: 'uniform' 109 | 110 | } 111 | 112 | } 113 | 114 | ] 115 | 116 | } ); 117 | 118 | let layout: GPUPipelineLayout = this.device.createPipelineLayout( { 119 | 120 | bindGroupLayouts: [ this.uniformGroupLayout ] 121 | 122 | } ); 123 | 124 | let vxModule: GPUShaderModule = this.device.createShaderModule( { 125 | 126 | code: vxCode 127 | 128 | } ); 129 | 130 | let fxModule: GPUShaderModule = this.device.createShaderModule( { 131 | 132 | code: fxCode 133 | 134 | } ); 135 | 136 | this.renderPipeline = this.device.createRenderPipeline( { 137 | 138 | layout: layout, 139 | 140 | vertex: { 141 | 142 | buffers: [ 143 | 144 | { 145 | 146 | arrayStride: 4 * 3, 147 | 148 | attributes: [ 149 | 150 | // position 151 | 152 | { 153 | 154 | shaderLocation: 0, 155 | 156 | offset: 0, 157 | 158 | format: 'float32x3' 159 | 160 | } 161 | 162 | ], 163 | 164 | stepMode: 'vertex' 165 | 166 | }, 167 | 168 | { 169 | 170 | arrayStride: 4 * 4, 171 | 172 | attributes: [ 173 | 174 | // color 175 | 176 | { 177 | 178 | shaderLocation: 1, 179 | 180 | offset: 0, 181 | 182 | format: 'float32x4' 183 | 184 | } 185 | 186 | ], 187 | 188 | stepMode: 'vertex' 189 | 190 | }, 191 | 192 | ], 193 | 194 | module: vxModule, 195 | 196 | entryPoint: 'main' 197 | 198 | }, 199 | 200 | fragment: { 201 | 202 | module: fxModule, 203 | 204 | entryPoint: 'main', 205 | 206 | targets: [ 207 | 208 | { 209 | 210 | format: this.format 211 | 212 | } 213 | 214 | ] 215 | 216 | }, 217 | 218 | primitive: { 219 | 220 | topology: 'triangle-list' 221 | 222 | } 223 | 224 | } ); 225 | 226 | this.renderBundleEncoder.setPipeline( this.renderPipeline ); 227 | 228 | } 229 | 230 | private _CreateGPUBuffer( typedArray: TypedArray, usage: GPUBufferUsageFlags ) { 231 | 232 | let gpuBuffer = this.device.createBuffer( { 233 | 234 | size: typedArray.byteLength, 235 | 236 | usage: usage | GPUBufferUsage.COPY_DST, 237 | 238 | mappedAtCreation: true 239 | 240 | } ); 241 | 242 | let constructor = typedArray.constructor as new ( buffer: ArrayBuffer ) => TypedArray; 243 | 244 | let view = new constructor( gpuBuffer.getMappedRange() ); 245 | 246 | view.set( typedArray, 0 ); 247 | 248 | gpuBuffer.unmap(); 249 | 250 | return gpuBuffer; 251 | 252 | } 253 | 254 | public InitGPUBufferWithMultiBuffers( vxArray: Float32Array, colorArray: Float32Array, idxArray: Uint32Array, mxArray: Float32Array ) { 255 | 256 | let vertexBuffer = this._CreateGPUBuffer( vxArray, GPUBufferUsage.VERTEX ); 257 | 258 | this.renderBundleEncoder.setVertexBuffer( 0, vertexBuffer ); 259 | 260 | let colorBuffer = this._CreateGPUBuffer( colorArray, GPUBufferUsage.VERTEX ); 261 | 262 | this.renderBundleEncoder.setVertexBuffer( 1, colorBuffer, 0 ); 263 | 264 | let indexBuffer = this._CreateGPUBuffer( idxArray, GPUBufferUsage.INDEX ); 265 | 266 | this.renderBundleEncoder.setIndexBuffer( indexBuffer, 'uint32' ); 267 | 268 | let uniformBuffer = this._CreateGPUBuffer( mxArray, GPUBufferUsage.UNIFORM ); 269 | 270 | let uniformBindGroup = this.device.createBindGroup( { 271 | 272 | layout: this.uniformGroupLayout, 273 | 274 | entries: [ { 275 | 276 | binding: 0, 277 | 278 | resource: { buffer: uniformBuffer } 279 | 280 | } ] 281 | 282 | } ); 283 | 284 | this.renderBundleEncoder.setBindGroup( 0, uniformBindGroup ); 285 | 286 | return { uniformBuffer }; 287 | 288 | } 289 | 290 | public Draw( indexCount: number, instanceCount: number = 3 ) { 291 | 292 | this.renderBundleEncoder.drawIndexed( indexCount, instanceCount, 0, 0, 0 ); 293 | 294 | } 295 | 296 | public Present( renderBundle: GPURenderBundle, clearColor: GPUColorDict ) { 297 | 298 | this.commandEncoder = this.device.createCommandEncoder(); 299 | 300 | let renderPassDescriptor: GPURenderPassDescriptor = { 301 | 302 | colorAttachments: [ { 303 | 304 | view: this.context.getCurrentTexture().createView(), 305 | 306 | loadValue: clearColor, 307 | 308 | storeOp: 'store' 309 | 310 | } ] 311 | 312 | } 313 | 314 | this.renderPassEncoder = this.commandEncoder.beginRenderPass( renderPassDescriptor ); 315 | 316 | this.renderPassEncoder.executeBundles( [ renderBundle ] ); 317 | 318 | this.renderPassEncoder.endPass(); 319 | 320 | this.device.queue.submit( [ this.commandEncoder.finish() ] ); 321 | 322 | } 323 | 324 | public RunRenderLoop( fn: Function ) { 325 | 326 | fn(); 327 | 328 | requestAnimationFrame( () => this.RunRenderLoop( fn ) ); 329 | 330 | } 331 | 332 | } -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lesson X - Render Bundle 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from './app'; 2 | import vxCode from './shader/vertex.wgsl'; 3 | import fxCode from './shader/fragment.wgsl' 4 | import { PerspectiveCamera, Matrix4, Vector3 } from 'three'; 5 | 6 | const triangleVertexPositon = new Float32Array( [ 7 | 8 | 0.0, 1.0, 0.0, 9 | -1.0, -1.0, 0.0, 10 | 1.0, -1.0, 0.0, 11 | 12 | ] ); 13 | 14 | const triangleVertexColor = new Float32Array( [ 15 | 16 | 1.0, 0.0, 0.0, 1.0, 17 | 0.0, 1.0, 0.0, 1.0, 18 | 0.0, 0.0, 1.0, 1.0 19 | 20 | ] ); 21 | 22 | const triangleIndex = new Uint32Array( [ 0, 1, 2 ] ); 23 | 24 | const triangleMVMatrix = new Matrix4(); 25 | 26 | let main = async () => { 27 | 28 | let camera = new PerspectiveCamera( 45, document.body.clientWidth / document.body.clientHeight, 0.1, 100 ); 29 | 30 | let pMatrix = camera.projectionMatrix; 31 | 32 | let backgroundColor = { r: 0, g: 0, b: 0, a: 1.0 }; 33 | 34 | let app = new App(); 35 | 36 | app.CreateCanvas( document.body ) 37 | 38 | await app.InitWebGPU(); 39 | 40 | app.InitRenderPass(); 41 | 42 | app.InitPipelineWitMultiBuffers( vxCode, fxCode ); 43 | 44 | let lastTime = 0, rTri = 0; 45 | 46 | let animate = () => { 47 | 48 | let timeNow = performance.now(); 49 | 50 | if ( lastTime != 0 ) { 51 | 52 | let elapsed = timeNow - lastTime; 53 | 54 | rTri += ( Math.PI / 180 * 90 * elapsed ) / 1000.0; 55 | 56 | } 57 | 58 | lastTime = timeNow; 59 | } 60 | 61 | app.renderBundleEncoder.setPipeline( app.renderPipeline ); 62 | 63 | triangleMVMatrix.makeTranslation( -2, 0, -4 ).multiply( new Matrix4().makeRotationX( rTri ) ); 64 | 65 | let triangleUniformBufferView = new Float32Array( pMatrix.toArray().concat( triangleMVMatrix.toArray() ) ); 66 | 67 | let { uniformBuffer } = app.InitGPUBufferWithMultiBuffers( triangleVertexPositon, triangleVertexColor, triangleIndex, triangleUniformBufferView ); 68 | 69 | app.Draw( triangleIndex.length, 3 ); 70 | 71 | let renderBundle: GPURenderBundle = app.renderBundleEncoder.finish(); 72 | 73 | app.RunRenderLoop( () => { 74 | 75 | animate(); 76 | 77 | triangleMVMatrix.makeTranslation( -2, 0, -4 ).multiply( new Matrix4().makeRotationX( rTri ) ); 78 | 79 | let triangleUniformBufferView = new Float32Array( pMatrix.toArray().concat( triangleMVMatrix.toArray() ) ); 80 | 81 | app.device.queue.writeBuffer( uniformBuffer, 0, triangleUniformBufferView ); 82 | 83 | app.Present( renderBundle, backgroundColor ); 84 | 85 | }) 86 | 87 | 88 | } 89 | 90 | window.addEventListener( 'DOMContentLoaded', main ); -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/src/shader/fragment.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `@stage(fragment) 3 | fn main( 4 | @location(0) vColor: vec4 5 | ) -> @location(0) vec4 { 6 | return vColor; 7 | }`; -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/src/shader/vertex.wgsl.ts: -------------------------------------------------------------------------------- 1 | export default 2 | `struct Uniforms { 3 | @size(64) uPMatrix: mat4x4; 4 | @size(64) uMVMatrix: mat4x4; 5 | }; 6 | 7 | struct Output { 8 | @location(0) vColor: vec4; 9 | @builtin(position) Position: vec4; 10 | }; 11 | 12 | @group(0) @binding(0) 13 | var uniforms: Uniforms; 14 | 15 | @stage(vertex) 16 | fn main( 17 | @builtin(instance_index) instanceIdx : u32, 18 | @location(0) aVertexPosition: vec3, 19 | @location(1) aVertexColor: vec4 20 | ) -> Output { 21 | var output: Output; 22 | let i: f32 = f32(instanceIdx); 23 | let pos: vec4 = vec4(aVertexPosition.x + i / 0.5, aVertexPosition.y, aVertexPosition.z, 1.0); 24 | output.Position = uniforms.uPMatrix * uniforms.uMVMatrix * pos; 25 | output.vColor = aVertexColor; 26 | return output; 27 | }`; -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ "src" ], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "ESNEXT", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 8 | "lib": [ "DOM", "ESNEXT" ], /* Specify library files to be included in the compilation. */ 9 | "allowJs": true, /* Allow javascript files to be compiled. */ 10 | "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | // "outDir": "./", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | "strictNullChecks": false, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | 42 | /* Module Resolution Options */ 43 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | "types": [ "@webgpu/types" ], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LessonX_RenderBundle/Code/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | root: "./src/", 4 | 5 | build: { 6 | 7 | outDir: "./dist/" 8 | 9 | }, 10 | 11 | server: { 12 | 13 | watch: { 14 | 15 | usePolling: true 16 | 17 | } 18 | 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LearningWebGPU 教程 2 | 3 | 本教程受原本的 LearningWebGL.com 中的 WebGL 教程启发。 4 | 5 | @deprecated ~~`glsl-spirv` 分支使用 GLSL->SPIR-V 作为着色器语言,使用 [@webgpu/glslang](https://www.npmjs.com/package/@webgpu/glslang) 编译成字节码。~~ 6 | 7 | `wgsl` 分支使用 [WGSL](https://gpuweb.github.io/gpuweb/wgsl.html) 作为着色器语言。 8 | 9 | ## WebGPU 标准释义 - 中文版(非官方,自翻版) 10 | 11 | [点击这里阅读](https://github.com/hjlld/LearningWebGPU/blob/wgsl/gpuweb-explainer/explainer.md) 12 | 13 | 14 | ## 教程目录 15 | 16 | - 第 0 课 - 全新的开始 17 | 18 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson0_Whole_new_start/Code) 19 | - [教程](https://github.com/hjlld/LearningWebGPU/blob/wgsl/Lesson0_Whole_new_start/Tutorial/Lesson0_Whole_new_start.md) - 完结 20 | 21 | - 第 1 课 - 三角和方块的故事 22 | 23 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson1_Triangle_and_square/Code) 24 | - [教程](https://github.com/hjlld/LearningWebGPU/blob/wgsl/Lesson1_Triangle_and_square/Tutorial/Lesson1_Triangle_and_square.md) - 完结 25 | 26 | - 第 2 课 - 添加颜色 27 | 28 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson2_Add_Color/Code) 29 | - [教程](https://github.com/hjlld/LearningWebGPU/blob/wgsl/Lesson2_Add_Color/Tutorial/Lesson2_Add_colors.md) - 完结 30 | 31 | - 第 3 课 - 动起来 32 | 33 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson3_Animate/Code) 34 | - [教程] 35 | 36 | - 第 4 课 - 真正的 3D 物体 37 | 38 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson4_Someting_real_3D/Code) 39 | - [教程] 40 | 41 | - 第 5 课 - 引入纹理 42 | 43 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson5_Texture/Code) 44 | - [教程] 45 | 46 | - 第 6 课 - 键盘输入和纹理过滤 47 | 48 | - [代码](https://github.com/hjlld/LearningWebGPU/tree/wgsl/Lesson6_Interactive_and_texture_filter/Code) 49 | - [教程] --------------------------------------------------------------------------------