├── .nvmrc ├── example ├── mesh │ ├── axes.html │ ├── mesh.html │ └── skybox.html ├── material │ ├── pbrMaterial.html │ └── blinn-phongMaterial.html ├── lia.png ├── textures │ ├── uv.jpg │ ├── Di-3d.png │ ├── webgpu.png │ ├── brdfLUT.png │ ├── city │ │ ├── nx.png │ │ ├── ny.png │ │ ├── nz.png │ │ ├── px.png │ │ ├── py.png │ │ └── pz.png │ ├── NormalMap.png │ ├── SpecularMap.png │ ├── cubemap │ │ ├── nx.png │ │ ├── ny.png │ │ ├── nz.png │ │ ├── px.png │ │ ├── py.png │ │ └── pz.png │ ├── skybox │ │ ├── nx.png │ │ ├── ny.png │ │ ├── nz.png │ │ ├── px.png │ │ ├── py.png │ │ └── pz.png │ ├── brick_diffuse.jpg │ ├── papermill │ │ ├── nx.jpg │ │ ├── ny.jpg │ │ ├── nz.jpg │ │ ├── px.jpg │ │ ├── py.jpg │ │ ├── pz.jpg │ │ └── diffuse │ │ │ ├── nx.jpg │ │ │ ├── ny.jpg │ │ │ ├── nz.jpg │ │ │ ├── px.jpg │ │ │ ├── py.jpg │ │ │ └── pz.jpg │ ├── DisplacementMap.png │ ├── environment │ │ ├── nx.jpg │ │ ├── ny.jpg │ │ ├── nz.jpg │ │ ├── px.jpg │ │ ├── py.jpg │ │ ├── pz.jpg │ │ └── diffuse │ │ │ ├── bakedDiffuse_01.jpg │ │ │ ├── bakedDiffuse_02.jpg │ │ │ ├── bakedDiffuse_03.jpg │ │ │ ├── bakedDiffuse_04.jpg │ │ │ ├── bakedDiffuse_05.jpg │ │ │ └── bakedDiffuse_06.jpg │ ├── AmbientOcclusionMap.png │ ├── normal │ │ ├── floorsDiffuse.jpg │ │ └── floorsNormal.jpg │ └── royal_esplanade_1k │ │ ├── nx.png │ │ ├── ny.png │ │ ├── nz.png │ │ ├── px.png │ │ ├── py.png │ │ └── pz.png ├── asset │ └── model │ │ └── dragon.glb ├── lib │ └── esm │ │ ├── index.js │ │ └── common.js ├── geometry │ └── box.html └── light │ ├── ambient.html │ └── spotLight.html ├── .vscode ├── settings.json └── extensions.json ├── tests └── README.md ├── src ├── shader │ ├── shaderChunk │ │ ├── attribute │ │ │ ├── FragOutput.wgsl │ │ │ ├── PointFragInput.wgsl │ │ │ ├── PointVertOutput.wgsl │ │ │ ├── FragInput.wgsl │ │ │ ├── VertexOutput.wgsl │ │ │ ├── PointVertInput.wgsl │ │ │ ├── index.ts │ │ │ └── VertexInput.wgsl │ │ ├── instance │ │ │ ├── instanceVertMain.wgsl │ │ │ ├── index.ts │ │ │ └── instanceVertHeader.wgsl │ │ ├── struct │ │ │ ├── index.ts │ │ │ └── PbrMaterialStruct.wgsl │ │ ├── common │ │ │ ├── SystemUniform.wgsl │ │ │ ├── index.ts │ │ │ └── TextureAndSamplerDefine.wgsl │ │ ├── skin │ │ │ ├── skinVertMain.wgsl │ │ │ ├── index.ts │ │ │ ├── getSkinMatrix.wgsl │ │ │ └── skinVertHeader.wgsl │ │ ├── phong │ │ │ ├── index.ts │ │ │ ├── phongUtils.wgsl │ │ │ ├── phongFunction.wgsl │ │ │ └── blinn_phong.wgsl │ │ ├── normal │ │ │ ├── index.ts │ │ │ ├── getNormalByNormalTexture.wgsl │ │ │ ├── getNormal.wgsl │ │ │ └── getTBN.wgsl │ │ ├── pbr │ │ │ ├── index.ts │ │ │ ├── ibl.wgsl │ │ │ ├── pbrStruct.wgsl │ │ │ └── pbrUtils.wgsl │ │ ├── ShaderChunk.ts │ │ └── light │ │ │ └── lightCommon.wgsl │ ├── shadow │ │ ├── shadowMapFrag.wgsl │ │ ├── index.ts │ │ ├── shadowMapDebuggerVert.wgsl │ │ ├── shadowMapDebuggerFrag.wgsl │ │ └── shadowMapVert.wgsl │ ├── material │ │ ├── colorFrag.wgsl │ │ ├── quadFrag.wgsl │ │ ├── quadVert.wgsl │ │ ├── sprite_fs.wgsl │ │ ├── point_fs.wgsl │ │ ├── skyBoxFrag.wgsl │ │ ├── colorVert.wgsl │ │ ├── skyBoxVert.wgsl │ │ ├── pbr_vs.wgsl │ │ ├── point_vs.wgsl │ │ ├── phongVert.wgsl │ │ ├── sprite_vs.wgsl │ │ └── phongFrag.wgsl │ ├── postProcess │ │ ├── blend │ │ │ └── blendFrag.wgsl │ │ ├── convolution │ │ │ └── convolutionVert.wgsl │ │ ├── bloom │ │ │ ├── LuminosityHigh.wgsl │ │ │ └── Blur.wgsl │ │ └── fxaa │ │ │ └── fxaa.wgsl │ └── README.md ├── render │ ├── Command.ts │ ├── BindGroupEntity.ts │ ├── StorageTexture.ts │ ├── ComputePassEncoder.ts │ ├── Sampler.ts │ ├── PipelineLayout.ts │ ├── BindGroupLayoutEntry.ts │ ├── ComputeCommand.ts │ ├── IndexBuffer.ts │ ├── BindGroupLayout.ts │ ├── Attachment.ts │ ├── RenderPass.ts │ ├── BindGroup.ts │ ├── QuerySet.ts │ ├── RenderTarget.ts │ └── VertexBuffer.ts ├── wgsl.d.ts ├── utils │ ├── defined.ts │ ├── loadGlslangModule.ts │ ├── createGuid.ts │ ├── defaultValue.ts │ ├── destroyObject.ts │ ├── request.ts │ ├── TypeInfer.ts │ ├── utils.ts │ └── combine.ts ├── core │ ├── IClone.ts │ ├── MeshManger.ts │ ├── FrameState.ts │ ├── EventDispatcher.ts │ ├── TextureCache.ts │ ├── Frustum.ts │ └── RenderObject.ts ├── loader │ ├── gltf │ │ ├── types │ │ │ └── gltfType.ts │ │ ├── libs │ │ │ ├── AnimationChannelTarget.ts │ │ │ ├── AnimationChannel.ts │ │ │ ├── Animation.ts │ │ │ └── Accessor.ts │ │ └── enum │ │ │ └── GLTFEnum.ts │ └── CubeTextureLoader.ts ├── renderpipeline │ ├── IBaseRenderLine.ts │ ├── DeferredRenderLine.ts │ └── ForwardRenderLine.ts ├── light │ ├── LightProbe.ts │ ├── AmbientLight.ts │ ├── shadows │ │ ├── SpotLightShadow.ts │ │ ├── DirectionalLightShadow.ts │ │ ├── DirectionalLightCascadedShadow.ts │ │ ├── BaseShadow.ts │ │ └── PointLightShadow.ts │ ├── HemisphereLightProbe.ts │ ├── PointLight.ts │ ├── DirectionalLight.ts │ └── Light.ts ├── type │ └── UpdateParams.ts ├── geometry │ ├── SpriteGeometry.ts │ ├── BoxGeometry.ts │ ├── PointGeometry.ts │ ├── SkyBoxGeometry.ts │ ├── SphereGeometry.ts │ └── PlaneGeometry.ts ├── mesh │ ├── Instance.ts │ ├── SkyBox.ts │ ├── Sprite.ts │ ├── Axes.ts │ ├── Points.ts │ ├── Node.ts │ ├── Mesh.ts │ └── SKinMesh.ts ├── material │ ├── ColorMaterial.ts │ ├── SpriteMaterial.ts │ ├── PointMaterial.ts │ ├── ShaderMaterial.ts │ ├── SkyBoxMaterial.ts │ └── BlinnPhongMaterial.ts ├── math │ ├── Ray.ts │ └── Spherical.ts ├── pass │ ├── RenderPass.ts │ └── BasicPass.ts ├── post-process │ ├── PostEffectCollection.ts │ ├── ResolvePostEffect.ts │ └── PostEffect.ts ├── camera │ ├── PerspectiveCamera.ts │ ├── OrthographicCamera.ts │ ├── PointLightShadowCamera.ts │ └── Camera.ts └── helper │ └── ShadowMapDebugger.ts ├── .npmrc ├── .husky ├── pre-commit └── commit-msg ├── .eslintignore ├── .npmignore ├── .gitignore ├── .commitlintrc.json ├── .github └── workflows │ └── deploy.yml ├── .prettierrc ├── LICENSE ├── rollup.config.js ├── tsconfig.json ├── hooks └── check-keyword.sh └── .eslintrc.json /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.16.0 -------------------------------------------------------------------------------- /example/mesh/axes.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/mesh/mesh.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /example/material/pbrMaterial.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # 待补充测试内容 2 | -------------------------------------------------------------------------------- /example/material/blinn-phongMaterial.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/FragOutput.wgsl: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | registry=https://registry.npmmirror.com 3 | -------------------------------------------------------------------------------- /src/render/Command.ts: -------------------------------------------------------------------------------- 1 | export interface Command { 2 | render(): void; 3 | } 4 | -------------------------------------------------------------------------------- /example/lia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/lia.png -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /example/textures/uv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/uv.jpg -------------------------------------------------------------------------------- /src/wgsl.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.wgsl" { 2 | const value: string; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /static/ 6 | /node_modules/ 7 | .eslintrc.json -------------------------------------------------------------------------------- /example/textures/Di-3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/Di-3d.png -------------------------------------------------------------------------------- /example/textures/webgpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/webgpu.png -------------------------------------------------------------------------------- /example/textures/brdfLUT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/brdfLUT.png -------------------------------------------------------------------------------- /example/textures/city/nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/nx.png -------------------------------------------------------------------------------- /example/textures/city/ny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/ny.png -------------------------------------------------------------------------------- /example/textures/city/nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/nz.png -------------------------------------------------------------------------------- /example/textures/city/px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/px.png -------------------------------------------------------------------------------- /example/textures/city/py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/py.png -------------------------------------------------------------------------------- /example/textures/city/pz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/city/pz.png -------------------------------------------------------------------------------- /example/asset/model/dragon.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/asset/model/dragon.glb -------------------------------------------------------------------------------- /example/textures/NormalMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/NormalMap.png -------------------------------------------------------------------------------- /example/textures/SpecularMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/SpecularMap.png -------------------------------------------------------------------------------- /example/textures/cubemap/nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/nx.png -------------------------------------------------------------------------------- /example/textures/cubemap/ny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/ny.png -------------------------------------------------------------------------------- /example/textures/cubemap/nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/nz.png -------------------------------------------------------------------------------- /example/textures/cubemap/px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/px.png -------------------------------------------------------------------------------- /example/textures/cubemap/py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/py.png -------------------------------------------------------------------------------- /example/textures/cubemap/pz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/cubemap/pz.png -------------------------------------------------------------------------------- /example/textures/skybox/nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/nx.png -------------------------------------------------------------------------------- /example/textures/skybox/ny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/ny.png -------------------------------------------------------------------------------- /example/textures/skybox/nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/nz.png -------------------------------------------------------------------------------- /example/textures/skybox/px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/px.png -------------------------------------------------------------------------------- /example/textures/skybox/py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/py.png -------------------------------------------------------------------------------- /example/textures/skybox/pz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/skybox/pz.png -------------------------------------------------------------------------------- /example/textures/brick_diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/brick_diffuse.jpg -------------------------------------------------------------------------------- /example/textures/papermill/nx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/nx.jpg -------------------------------------------------------------------------------- /example/textures/papermill/ny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/ny.jpg -------------------------------------------------------------------------------- /example/textures/papermill/nz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/nz.jpg -------------------------------------------------------------------------------- /example/textures/papermill/px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/px.jpg -------------------------------------------------------------------------------- /example/textures/papermill/py.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/py.jpg -------------------------------------------------------------------------------- /example/textures/papermill/pz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/pz.jpg -------------------------------------------------------------------------------- /src/utils/defined.ts: -------------------------------------------------------------------------------- 1 | export default function defined(value) { 2 | return value !== undefined && value !== null; 3 | } 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /example/textures/DisplacementMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/DisplacementMap.png -------------------------------------------------------------------------------- /example/textures/environment/nx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/nx.jpg -------------------------------------------------------------------------------- /example/textures/environment/ny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/ny.jpg -------------------------------------------------------------------------------- /example/textures/environment/nz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/nz.jpg -------------------------------------------------------------------------------- /example/textures/environment/px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/px.jpg -------------------------------------------------------------------------------- /example/textures/environment/py.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/py.jpg -------------------------------------------------------------------------------- /example/textures/environment/pz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/pz.jpg -------------------------------------------------------------------------------- /example/textures/AmbientOcclusionMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/AmbientOcclusionMap.png -------------------------------------------------------------------------------- /example/textures/normal/floorsDiffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/normal/floorsDiffuse.jpg -------------------------------------------------------------------------------- /example/textures/normal/floorsNormal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/normal/floorsNormal.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/nx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/nx.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/ny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/ny.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/nz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/nz.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/px.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/py.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/py.jpg -------------------------------------------------------------------------------- /example/textures/papermill/diffuse/pz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/papermill/diffuse/pz.jpg -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/nx.png -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/ny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/ny.png -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/nz.png -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/px.png -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/py.png -------------------------------------------------------------------------------- /example/textures/royal_esplanade_1k/pz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/royal_esplanade_1k/pz.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | public/workers 4 | example 5 | *.mp4 6 | *.flv 7 | *.rar 8 | *.zip 9 | *.7z 10 | .catch/** -------------------------------------------------------------------------------- /src/core/IClone.ts: -------------------------------------------------------------------------------- 1 | import { RenderObjectType } from "./WebGPUTypes"; 2 | 3 | export default interface IClone { 4 | type: RenderObjectType; 5 | } 6 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/instance/instanceVertMain.wgsl: -------------------------------------------------------------------------------- 1 | #if USE_INSTANCE 2 | modelMatrix = instancesUniform.instanceMatrixs[input.instanceIdx]; 3 | #endif 4 | -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_01.jpg -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_02.jpg -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_03.jpg -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_04.jpg -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_05.jpg -------------------------------------------------------------------------------- /example/textures/environment/diffuse/bakedDiffuse_06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEngine-js/GEngine/HEAD/example/textures/environment/diffuse/bakedDiffuse_06.jpg -------------------------------------------------------------------------------- /src/shader/shaderChunk/struct/index.ts: -------------------------------------------------------------------------------- 1 | import PbrMaterialStruct from "./PbrMaterialStruct.wgsl"; 2 | 3 | const struct = { 4 | PbrMaterialStruct 5 | }; 6 | 7 | export default struct; 8 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/PointFragInput.wgsl: -------------------------------------------------------------------------------- 1 | struct PointFragInput{ 2 | @location(0) uv : vec2 , 3 | @location(1) color : vec3 , 4 | @location(2) size : f32, 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "clonecode.clonecode-wgsl", 4 | "ritwickdey.LiveServer", 5 | "esbenp.prettier-vscode", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/loader/gltf/types/gltfType.ts: -------------------------------------------------------------------------------- 1 | export enum Type2NumOfComponent { 2 | "SCALAR" = 1, 3 | "VEC2" = 2, 4 | "VEC3" = 3, 5 | "VEC4" = 4, 6 | "MAT2" = 4, 7 | "MAT3" = 9, 8 | "MAT4" = 16 9 | } 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/common/SystemUniform.wgsl: -------------------------------------------------------------------------------- 1 | struct SystemUniform { 2 | projectionMatrix : mat4x4 , 3 | viewMatrix : mat4x4 , 4 | inverseViewMatrix : mat4x4 , 5 | cameraPosition : vec3 , 6 | }; 7 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/PointVertOutput.wgsl: -------------------------------------------------------------------------------- 1 | struct PointVertOutput{ 2 | @builtin(position) position : vec4 , 3 | @location(0) uv : vec2 , 4 | @location(1) color : vec3 , 5 | @location(2) size : f32, 6 | } 7 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/skin/skinVertMain.wgsl: -------------------------------------------------------------------------------- 1 | #if HAS_SKIN 2 | modelMatrix = getSkinMatrix(input.joint0, input.weight0); 3 | vNormalView = normalize((materialUniform.normalMatrix * modelMatrix * vec4 (input.normal, 0.0)).xyz); 4 | #endif 5 | -------------------------------------------------------------------------------- /src/shader/shadow/shadowMapFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | @builtin(position) position : vec4 , 3 | @location(0) color : vec4 , 4 | }; 5 | @fragment 6 | fn main(input : VertexOutput) -> @location(0) vec4 { 7 | return input.color; 8 | } 9 | -------------------------------------------------------------------------------- /src/shader/material/colorFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | @builtin(position) position : vec4 , 3 | @location(0) color : vec4 , 4 | }; 5 | 6 | @fragment 7 | fn main(input : VertexOutput) -> @location(0) vec4 { 8 | return input.color; 9 | } 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/common/index.ts: -------------------------------------------------------------------------------- 1 | import SystemUniform from "./SystemUniform.wgsl"; 2 | import TextureAndSamplerDefine from "./TextureAndSamplerDefine.wgsl"; 3 | 4 | const common = { 5 | SystemUniform, 6 | TextureAndSamplerDefine 7 | }; 8 | 9 | export default common; 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/instance/index.ts: -------------------------------------------------------------------------------- 1 | import instanceVertHeader from "./instanceVertHeader.wgsl"; 2 | import instanceVertMain from "./instanceVertMain.wgsl"; 3 | 4 | const instance = { 5 | instanceVertHeader, 6 | instanceVertMain 7 | }; 8 | 9 | export default instance; 10 | -------------------------------------------------------------------------------- /src/utils/loadGlslangModule.ts: -------------------------------------------------------------------------------- 1 | export async function loadGlslangModule(): Promise { 2 | // @ts-ignore 3 | const glslangModule = await import("https://unpkg.com/@webgpu/glslang@0.0.15/dist/web-devel/glslang.js"); 4 | const glslang = await glslangModule.default(); 5 | return glslang; 6 | } 7 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/instance/instanceVertHeader.wgsl: -------------------------------------------------------------------------------- 1 | #if USE_INSTANCE 2 | struct InstancesUniform { 3 | instanceMatrixs : array, instanceCount>, 4 | }; 5 | @group(0) @binding(instanceMatrixsBufferBinding) var instancesUniform : InstancesUniform; 6 | #endif 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/workers 3 | *.mp4 4 | *.flv 5 | *.rar 6 | *.zip 7 | *.7z 8 | .catch/** 9 | 10 | .DS_Store 11 | 12 | # Log files 13 | npm-debug.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /src/loader/gltf/libs/AnimationChannelTarget.ts: -------------------------------------------------------------------------------- 1 | import Node from "../../../mesh/Node"; 2 | 3 | export class AnimationChannelTarget { 4 | node: Node; 5 | path: "translation" | "rotation" | "scale" | "weights"; 6 | constructor(node, path) { 7 | this.node = node; 8 | this.path = path; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/phong/index.ts: -------------------------------------------------------------------------------- 1 | import blinn_phong from "./blinn_phong.wgsl"; 2 | import phongFunction from "./phongFunction.wgsl"; 3 | import phongUtils from "./phongUtils.wgsl"; 4 | 5 | const phong = { 6 | blinn_phong, 7 | phongFunction, 8 | phongUtils 9 | }; 10 | 11 | export default phong; 12 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/skin/index.ts: -------------------------------------------------------------------------------- 1 | // import getSkinMatrix from "./getSkinMatrix.wgsl"; 2 | import skinVertHeader from "./skinVertHeader.wgsl"; 3 | import skinVertMain from "./skinVertMain.wgsl"; 4 | 5 | const skin = { 6 | // getSkinMatrix, 7 | skinVertHeader, 8 | skinVertMain 9 | }; 10 | 11 | export default skin; 12 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/normal/index.ts: -------------------------------------------------------------------------------- 1 | import getNormal from "./getNormal.wgsl"; 2 | import getNormalByNormalTexture from "./getNormalByNormalTexture.wgsl"; 3 | import getTBN from "./getTBN.wgsl"; 4 | 5 | const normalChunks = { 6 | getNormal, 7 | getNormalByNormalTexture, 8 | getTBN 9 | }; 10 | 11 | export default normalChunks; 12 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/normal/getNormalByNormalTexture.wgsl: -------------------------------------------------------------------------------- 1 | fn getNormalByNormalTexture(input:FragInput)->vec3{ 2 | var n:vec3 = textureSample(normalTexture,normalSampler, input.uv).rgb; 3 | let tbn:mat3x3 =getTBN(input); 4 | n = normalize(tbn * (2.0 * n - vec3(1.0))); 5 | n=n*(f32(input.frontFacing) * 2.0 - 1.0); 6 | return n; 7 | } -------------------------------------------------------------------------------- /src/renderpipeline/IBaseRenderLine.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import Texture from "../render/Texture"; 4 | export default interface IBaseRenderLine { 5 | render(frameState: FrameState, camera?: Camera): void; 6 | getOutputTexture(): Texture; 7 | setSize(width: number, height: number): void; 8 | } 9 | -------------------------------------------------------------------------------- /src/loader/gltf/libs/AnimationChannel.ts: -------------------------------------------------------------------------------- 1 | import { AnimationChannelTarget } from "./AnimationChannelTarget"; 2 | import { AnimationSampler } from "./AnimationSampler"; 3 | 4 | export class AnimationChannel { 5 | sampler: AnimationSampler; 6 | target: AnimationChannelTarget; 7 | // eslint-disable-next-line @typescript-eslint/no-empty-function 8 | constructor() {} 9 | } 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/pbr/index.ts: -------------------------------------------------------------------------------- 1 | import brdf from "./brdf.wgsl"; 2 | import ibl from "./ibl.wgsl"; 3 | import pbrStruct from "./pbrStruct.wgsl"; 4 | import pbrTexture from "./pbrTexture.wgsl"; 5 | import pbrUtils from "./pbrUtils.wgsl"; 6 | 7 | const pbr = { 8 | brdf, 9 | ibl, 10 | pbrStruct, 11 | pbrTexture, 12 | pbrUtils 13 | }; 14 | 15 | export default pbr; 16 | -------------------------------------------------------------------------------- /src/light/LightProbe.ts: -------------------------------------------------------------------------------- 1 | import SphericalHarmonics3 from "../math/SphericalHarmonics3"; 2 | import { Light } from "./Light"; 3 | 4 | export class LightProbe extends Light { 5 | public sh: SphericalHarmonics3; 6 | constructor(sh = new SphericalHarmonics3(), intensity = 1) { 7 | super(undefined, intensity); 8 | // this.type = "lightProbe"; 9 | this.sh = sh; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/createGuid.ts: -------------------------------------------------------------------------------- 1 | function createGuid() { 2 | // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 3 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 4 | const r = (Math.random() * 16) | 0; 5 | const v = c === "x" ? r : (r & 0x3) | 0x8; 6 | return v.toString(16); 7 | }); 8 | } 9 | export default createGuid; 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/FragInput.wgsl: -------------------------------------------------------------------------------- 1 | struct FragInput { 2 | @builtin(front_facing) frontFacing : bool, 3 | @location(0) worldPos : vec3 , 4 | @location(1) normal : vec3 , 5 | @location(2) uv : vec2 , 6 | @location(3) view : vec3 ,//Vector from vertex to camera. 7 | @location(4) color : vec4 , 8 | @location(5) viewPosition : vec3 , 9 | } 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/VertexOutput.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | @builtin(position) position : vec4 , 3 | @location(0) worldPos : vec3 , 4 | @location(1) normal : vec3 , 5 | @location(2) uv : vec2 , 6 | @location(3) view : vec3 ,//Vector from vertex to camera. 7 | @location(4) color : vec4 , 8 | @location(5) viewPosition : vec3 , 9 | } 10 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/normal/getNormal.wgsl: -------------------------------------------------------------------------------- 1 | fn getNormal(input:FragInput)->vec3{ 2 | var normal:vec3; 3 | #if HAS_NORMAL 4 | normal= input.normal; 5 | #else 6 | let pos_dx = dpdx(input.worldPos); 7 | let pos_dy = dpdy(input.worldPos); 8 | normal = normalize( cross(pos_dy, pos_dx) ); 9 | #endif 10 | return normal*(f32(input.frontFacing) * 2.0 - 1.0); 11 | } -------------------------------------------------------------------------------- /src/utils/defaultValue.ts: -------------------------------------------------------------------------------- 1 | function defaultValue(a, b) { 2 | if (a !== undefined && a !== null) { 3 | return a; 4 | } 5 | return b; 6 | } 7 | 8 | /** 9 | * A frozen empty object that can be used as the default value for options passed as 10 | * an object literal. 11 | * @type {Object} 12 | * @memberof defaultValue 13 | */ 14 | defaultValue.EMPTY_OBJECT = Object.freeze({}); 15 | 16 | export default defaultValue; 17 | -------------------------------------------------------------------------------- /src/shader/shadow/index.ts: -------------------------------------------------------------------------------- 1 | import shadowMapDebuggerFrag from "./shadowMapDebuggerFrag.wgsl"; 2 | import shadowMapDebuggerVert from "./shadowMapDebuggerVert.wgsl"; 3 | import shadowMapFrag from "./shadowMapFrag.wgsl"; 4 | import shadowMapVert from "./shadowMapVert.wgsl"; 5 | 6 | const shadow = { 7 | shadowMapDebuggerFrag, 8 | shadowMapDebuggerVert, 9 | shadowMapFrag, 10 | shadowMapVert 11 | }; 12 | 13 | export default shadow; 14 | -------------------------------------------------------------------------------- /src/shader/material/quadFrag.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(1) var baseSampler : sampler; 2 | @group(0) @binding(0) var colorTexture : texture_2d; 3 | struct VertexOutput { 4 | @builtin(position) position : vec4 , 5 | @location(0) uv : vec2 , 6 | }; 7 | @fragment 8 | fn main(input : VertexOutput) -> @location(0) vec4 { 9 | return textureSample(colorTexture, baseSampler, vec2 (input.uv.x, 1.0 - input.uv.y)); 10 | } 11 | -------------------------------------------------------------------------------- /src/type/UpdateParams.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import Matrix4 from "../math/Matrix4"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | 6 | export type GeometryUpdateParams = { 7 | frameState?: FrameState; 8 | mesh?: Mesh; 9 | camera?: Camera; 10 | matrix?: Matrix4; 11 | }; 12 | export type MaterialUpdateParams = { 13 | frameState?: FrameState; 14 | mesh?: Mesh; 15 | }; 16 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/struct/PbrMaterialStruct.wgsl: -------------------------------------------------------------------------------- 1 | struct MaterialUniform { 2 | modelMatrix : mat4x4 , 3 | color : vec3 , 4 | opacity : f32, 5 | normalMatrix : mat4x4 , 6 | emissive : vec3 , 7 | metallic : f32, 8 | roughness : f32, 9 | #if USE_NORMALTEXTURE 10 | normalTextureScale : vec2 , 11 | #endif 12 | #if USE_AOTEXTURE 13 | occlusionStrength : f32, 14 | #endif 15 | } 16 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"], 3 | "rules": { 4 | "type-enum": [ 5 | 2, 6 | "always", 7 | [ 8 | "perf", 9 | "demo", 10 | "merge", 11 | "feat", 12 | "fix", 13 | "docs", 14 | "style", 15 | "refactor", 16 | "test", 17 | "revert", 18 | "build", 19 | "ci", 20 | "chore", 21 | "conflict", 22 | "delete", 23 | "release" 24 | ] 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/render/BindGroupEntity.ts: -------------------------------------------------------------------------------- 1 | import { BindGroupEntityOptions } from "../core/WebGPUTypes"; 2 | export default class BindGroupEntity { 3 | binding: number; 4 | resource: GPUBindingResource; 5 | constructor(options: BindGroupEntityOptions) { 6 | this.binding = options.binding; 7 | this.resource = options.resource; 8 | } 9 | public getGPUGroupEntity() { 10 | return { 11 | binding: this.binding, 12 | resource: this.resource 13 | }; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | workflow_dispatch: {} 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 16 17 | cache: npm 18 | - run: npm ci 19 | - name: Build 20 | run: npm run build -------------------------------------------------------------------------------- /example/lib/esm/index.js: -------------------------------------------------------------------------------- 1 | import * as glMatrix from "./common.js"; 2 | import * as mat2 from "./mat2.js"; 3 | import * as mat2d from "./mat2d.js"; 4 | import * as mat3 from "./mat3.js"; 5 | import * as mat4 from "./mat4.js"; 6 | import * as quat from "./quat.js"; 7 | import * as quat2 from "./quat2.js"; 8 | import * as vec2 from "./vec2.js"; 9 | import * as vec3 from "./vec3.js"; 10 | import * as vec4 from "./vec4.js"; 11 | export { glMatrix, mat2, mat2d, mat3, mat4, quat, quat2, vec2, vec3, vec4 }; 12 | -------------------------------------------------------------------------------- /src/shader/material/quadVert.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct VertexInput { 3 | @location(positionLocation) position : vec2 , 4 | } 5 | struct VertexOutput { 6 | @builtin(position) position : vec4 , 7 | @location(0) uv : vec2 , 8 | }; 9 | @vertex 10 | fn main(input : VertexInput) -> VertexOutput { 11 | var output : VertexOutput; 12 | output.uv = input.position * 0.5 + 0.5; 13 | output.position = vec4 (input.position, 0.0, 1.0); ; 14 | return output; 15 | } 16 | -------------------------------------------------------------------------------- /src/shader/shadow/shadowMapDebuggerVert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location(positionLocation) position : vec2 , 3 | } 4 | struct VertexOutput { 5 | @builtin(position) position : vec4 , 6 | @location(0) uv : vec2 , 7 | }; 8 | @vertex 9 | fn main(input : VertexInput) -> VertexOutput { 10 | var output : VertexOutput; 11 | output.uv = input.position * 0.5 + 0.5; 12 | output.position = vec4 (input.position, 0.0, 1.0); ; 13 | return output; 14 | } 15 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/PointVertInput.wgsl: -------------------------------------------------------------------------------- 1 | struct PointVertInput { 2 | @location(vertexPointLocation) vertexPoint : vec3 , 3 | @location(positionLocation) position : vec3 , 4 | @location(uvLocation) uv : vec2 , 5 | #if HAS_COLOR 6 | @location(colorLocation) color : vec3 , 7 | #endif 8 | #if VERTEX_SIZE 9 | @location(sizeLocation) size : f32, 10 | #endif 11 | #if USE_INSTANCE 12 | @builtin(instance_index) instanceIdx : u32 13 | #endif 14 | } 15 | -------------------------------------------------------------------------------- /src/render/StorageTexture.ts: -------------------------------------------------------------------------------- 1 | import { WebGPUTextureProps } from "../core/WebGPUTypes"; 2 | import Texture from "./Texture"; 3 | 4 | export class StorageTexture extends Texture { 5 | constructor(params: WebGPUTextureProps) { 6 | super(params); 7 | } 8 | get layoutType(): any { 9 | // const { access = StorageTextureAccess.WriteOnly, viewFormats, format } = this.textureProp; 10 | // return { 11 | // viewDimension: defaultValue(viewFormats, "2d"), 12 | // access, 13 | // format 14 | // }; 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/index.ts: -------------------------------------------------------------------------------- 1 | import FragInput from "./FragInput.wgsl"; 2 | import PointFragInput from "./PointFragInput.wgsl"; 3 | import PointVertInput from "./PointVertInput.wgsl"; 4 | import PointVertOutput from "./PointVertOutput.wgsl"; 5 | import VertexInput from "./VertexInput.wgsl"; 6 | import VertexOutput from "./VertexOutput.wgsl"; 7 | 8 | const attribute = { 9 | FragInput, 10 | PointFragInput, 11 | PointVertInput, 12 | PointVertOutput, 13 | VertexInput, 14 | VertexOutput 15 | }; 16 | 17 | export default attribute; 18 | -------------------------------------------------------------------------------- /src/renderpipeline/DeferredRenderLine.ts: -------------------------------------------------------------------------------- 1 | import Texture from "../render/Texture"; 2 | import IBaseRenderLine from "./IBaseRenderLine"; 3 | export default class DeferredRenderLine implements IBaseRenderLine { 4 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 5 | setSize(width: number, height: number): void { 6 | throw new Error("Method not implemented."); 7 | } 8 | getOutputTexture(): Texture { 9 | throw new Error("Method not implemented."); 10 | } 11 | render() { 12 | throw new Error("Method not implemented."); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/destroyObject.ts: -------------------------------------------------------------------------------- 1 | function returnTrue() { 2 | return true; 3 | } 4 | export function destroyObject(object) { 5 | // message =message||"This object was destroyed, i.e., destroy() was called."; 6 | 7 | function throwOnDestroyed() { 8 | throw new Error("This object was destroyed, i.e., destroy() was called."); 9 | } 10 | 11 | for (const key in object) { 12 | if (typeof object[key] === "function") { 13 | object[key] = throwOnDestroyed; 14 | } 15 | } 16 | 17 | object.isDestroyed = returnTrue; 18 | 19 | return undefined; 20 | } 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "htmlWhitespaceSensitivity": "css", 7 | "insertPragma": false, 8 | "jsxSingleQuote": false, 9 | "printWidth": 120, 10 | "proseWrap": "preserve", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleAttributePerLine": false, 15 | "singleQuote": false, 16 | "tabWidth": 4, 17 | "trailingComma": "none", 18 | "useTabs": true, 19 | "vueIndentScriptAndStyle": false 20 | } 21 | -------------------------------------------------------------------------------- /src/loader/gltf/enum/GLTFEnum.ts: -------------------------------------------------------------------------------- 1 | export const WebGPUSampler: { [key: string]: string | number } = { 2 | 9728: "nearest", 3 | 9729: "linear", 4 | 9984: "linear", 5 | 9985: "linear", 6 | 9986: "linear", 7 | 9987: "linear", 8 | 33071: "clamp-to-edge", 9 | 33648: "mirror-repeat", 10 | 10497: "repeat" 11 | }; 12 | export const ComponentType = { 13 | 5120: 1, 14 | 5121: 1, 15 | 5122: 2, 16 | 5123: 2, 17 | 5125: 4, 18 | 5126: 4 19 | }; 20 | export const CompononentCount = { 21 | SCALAR: 1, 22 | VEC2: 2, 23 | VEC3: 3, 24 | VEC4: 4, 25 | MAT2: 4, 26 | MAT3: 9, 27 | MAT4: 16 28 | }; 29 | -------------------------------------------------------------------------------- /src/shader/postProcess/blend/blendFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragInput { 2 | @location(0) uv : vec2 , 3 | }; 4 | @group(0) @binding({{tDiffuseBinding}}) var tDiffuse : texture_2d; 5 | @group(0) @binding({{baseColorTextureBinding}}) var baseColorTexture : texture_2d; 6 | @group(0) @binding({{tSamplerBinding}}) var tSampler : sampler; 7 | @fragment 8 | fn main(input : FragInput) -> @location(0) vec4 { 9 | let postColor : vec4 = textureSample(tDiffuse, tSampler, input.uv); 10 | let baseColor : vec4 = textureSample(baseColorTexture, tSampler, input.uv); 11 | return baseColor + postColor; 12 | } 13 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/attribute/VertexInput.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location(positionLocation) position : vec3 , 3 | #if HAS_NORMAL 4 | @location(normalLocation) normal : vec3 , 5 | #endif 6 | #if HAS_COLOR 7 | @location(colorLocation) color : vec3 , 8 | #endif 9 | #if HAS_UV 10 | @location(uvLocation) uv : vec2 , 11 | #endif 12 | #if HAS_SKIN 13 | @location(joint0Location) joint0 : vec4 , 14 | @location(weight0Location) weight0 : vec4 , 15 | #endif 16 | #if USE_INSTANCE 17 | @builtin(instance_index) instanceIdx : u32 18 | #endif 19 | } 20 | -------------------------------------------------------------------------------- /src/geometry/SpriteGeometry.ts: -------------------------------------------------------------------------------- 1 | import { InterleavedFloat32Attribute } from "../render/Attribute"; 2 | import Geometry from "./Geometry"; 3 | export class SpriteGeometry extends Geometry { 4 | constructor() { 5 | super({ 6 | type: "spriteGeometry" 7 | }); 8 | this.init(); 9 | } 10 | private init() { 11 | // xyz、uv 12 | const vertices = [-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]; 13 | const indices = [0, 1, 2, 0, 2, 3]; 14 | this.computeBoundingSphere(vertices, 5); 15 | this.setAttribute(new InterleavedFloat32Attribute(["position", "uv"], vertices, [3, 2])); 16 | this.setIndice(indices); 17 | this.count = indices.length; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/mesh/Instance.ts: -------------------------------------------------------------------------------- 1 | import RenderObject from "../core/RenderObject"; 2 | import Matrix4 from "../math/Matrix4"; 3 | import createGuid from "../utils/createGuid"; 4 | 5 | export class Instance extends RenderObject { 6 | public id: string | number; 7 | public visiblity: boolean; 8 | private _notUpdateMatrix: boolean; 9 | constructor() { 10 | super(); 11 | this._notUpdateMatrix = false; 12 | this.visiblity = false; 13 | this.id = createGuid(); 14 | } 15 | setMatrix4(mat4: Matrix4) { 16 | this.modelMatrix.set(mat4); 17 | this._notUpdateMatrix = true; 18 | } 19 | updateMatrix(matrix?: Matrix4) { 20 | if (this._notUpdateMatrix) return; 21 | super.updateMatrix(matrix); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/phong/phongUtils.wgsl: -------------------------------------------------------------------------------- 1 | struct BlinnPhongMaterial { 2 | diffuseColor : vec3 , 3 | specularColor : vec3 , 4 | specularShininess : f32, 5 | specularStrength : f32, 6 | }; 7 | const reciprocal_pi : f32 = 0.3183098861837907; 8 | fn pow2(x : f32) -> f32 { return x * x; } 9 | fn pow3(x : f32) -> f32 { return x * x*x; } 10 | fn pow4(x : f32) -> f32 { let x2 = x * x; return x2 * x2; } 11 | fn max3(v : vec3 ) -> f32 { return max(max(v.x, v.y), v.z); } 12 | fn average(v : vec3 ) -> f32 { 13 | let result = vec3 (0.3333333, 0.3333333, 0.3333333); 14 | return dot(v, result); 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | // export function requestWasmFile(url): Promise { 2 | // return fetch(url, { method: "get", responseType: "arraybuffer" }).then((res) => { 3 | // return res.arrayBuffer(); 4 | // }); 5 | // } 6 | export function getIamge(url: string) { 7 | return new Promise((resolve, reject) => { 8 | const image = new Image(); 9 | image.src = url; 10 | image.onload = () => { 11 | resolve(image); 12 | }; 13 | image.onerror = () => { 14 | reject(); 15 | }; 16 | }); 17 | } 18 | export async function getImageBitMap(url) { 19 | const response = await fetch(url); 20 | const blob = await response.blob(); 21 | return createImageBitmap(blob).then((imageBitmap) => { 22 | return imageBitmap; 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/shader/postProcess/convolution/convolutionVert.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct VertexInput { 3 | @location(0) position : vec2 , 4 | } 5 | struct VertexOutput { 6 | @builtin(position) position : vec4 , 7 | @location(0) uv : vec2 , 8 | }; 9 | struct ConvolutionUniforms { 10 | uImageIncrement : vec2 11 | }; 12 | @group(0) @binding(convolutionUniformsBinding) var convolutionUniforms : ConvolutionUniforms; 13 | @vertex 14 | fn main(input : VertexInput) -> VertexOutput { 15 | var output : VertexOutput; 16 | output.uv = (input.position * 0.5 + 0.5) - ((25.0 - 1.0) / 2.0) * convolutionUniforms.uImageIncrement; 17 | output.position = vec4 (input.position, 0.0, 1.0); ; 18 | return output; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/light/AmbientLight.ts: -------------------------------------------------------------------------------- 1 | import { LightType } from "../core/WebGPUTypes"; 2 | import Vector3 from "../math/Vector3"; 3 | import Vector4 from "../math/Vector4"; 4 | import { Light } from "./Light"; 5 | 6 | export class AmbientLight extends Light { 7 | private _colorAndIntensity: Vector4; 8 | constructor(color: Vector3, intensity: number) { 9 | super(color, intensity); 10 | this.lightType = LightType.AmbientLight; 11 | this._colorAndIntensity = new Vector4(color.x, color.y, color.z, intensity); 12 | } 13 | get ColorAndIntensity() { 14 | this._colorAndIntensity.set(this.color.x, this.color.y, this.color.z, this.intensity); 15 | return this._colorAndIntensity; 16 | } 17 | } 18 | // light.color ).multiplyScalar( light.intensity * scaleFactor ); 19 | -------------------------------------------------------------------------------- /src/shader/material/sprite_fs.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | struct SelfUniform { 3 | modelMatrix : mat4x4 , 4 | color : vec3 , 5 | rotation : f32, 6 | center : vec2 , 7 | opacity : f32, 8 | } 9 | @binding(spriteBinding) @group(0) var selfUniform : SelfUniform; 10 | #if USE_BASECOLORTEXTURE 11 | @group(0) @binding(baseColorSamplerBinding) var baseColorSampler : sampler; 12 | @group(0) @binding(baseColorTextureBinding) var baseColorTexture : texture_2d; 13 | #endif 14 | @fragment 15 | fn main(input : VertexOutput) -> @location(0) vec4 { 16 | #if USE_BASECOLORTEXTURE 17 | return textureSample(baseColorTexture, baseColorSampler, input.uv); 18 | #else 19 | return vec4 (selfUniform.color, selfUniform.opacity); 20 | #endif 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2022 GEngine-js 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /src/material/ColorMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import { ShaderDataEnum } from "../core/WebGPUTypes"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | import { ShaderSource } from "../shader/ShaderSource"; 6 | import { Material } from "./Material"; 7 | export default class ColorMaterial extends Material { 8 | constructor() { 9 | super(); 10 | this.type = "color"; 11 | this.shaderSource = new ShaderSource({ 12 | shaderId: this.type 13 | }); 14 | } 15 | update(frameState?: FrameState, mesh?: Mesh) { 16 | if (!this.shaderData || this.dirty) 17 | this.shaderData = ShaderDataFactory.createShaderData({ 18 | mesh, 19 | material: this, 20 | shaderDataEnum: ShaderDataEnum.COLOR 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/shader/material/point_fs.wgsl: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | struct SelfUniform { 4 | modelMatrix : mat4x4 , 5 | color : vec3 , 6 | size : f32, 7 | } 8 | @binding(pointBinding) @group(0) var selfUniform : SelfUniform; 9 | #if USE_BASECOLORTEXTURE 10 | @group(0) @binding(baseColorSamplerBinding) var baseColorSampler : sampler; 11 | @group(0) @binding(baseColorTextureBinding) var baseColorTexture : texture_2d; 12 | #endif 13 | @fragment 14 | fn main(input : PointFragInput) -> @location(0) vec4 { 15 | var color : vec4 = vec4 (selfUniform.color, 1.0); 16 | #if USE_BASECOLORTEXTURE 17 | color = textureSample(baseColorTexture, baseColorSampler, input.uv); 18 | #endif 19 | #if VERTEX_COLOR 20 | color = vec4 (input.color, 1.0); 21 | #endif 22 | return color; 23 | } 24 | -------------------------------------------------------------------------------- /src/light/shadows/SpotLightShadow.ts: -------------------------------------------------------------------------------- 1 | import PerspectiveCamera from "../../camera/PerspectiveCamera"; 2 | import Vector2 from "../../math/Vector2"; 3 | import { SpotLight } from "../SpotLight"; 4 | import { BaseShadow } from "./BaseShadow"; 5 | 6 | export class SpotLightShadow extends BaseShadow { 7 | public type: string; 8 | constructor() { 9 | const camera = new PerspectiveCamera(60, 1, 0.1, 500); 10 | super(new Vector2(1024, 1024), camera); 11 | this.type = "spotLightShadow"; 12 | super.init(); 13 | } 14 | 15 | public update(light: SpotLight) { 16 | this.updateMatrices(light); 17 | } 18 | 19 | updateMatrices(light: SpotLight) { 20 | this.camera.position.copy(light.position); 21 | const { x, y, z } = light.target; 22 | this.camera.lookAt(x, y, z); 23 | this.camera.updateMatrix(); 24 | this.vpMatrixDirty = true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/TypeInfer.ts: -------------------------------------------------------------------------------- 1 | import { TypedArray } from "../core/WebGPUTypes"; 2 | import Texture from "../render/Texture"; 3 | 4 | export function isTypeArray(array): array is TypedArray { 5 | return array.buffer != undefined; 6 | } 7 | export function isTexture(texture): texture is Texture { 8 | return texture instanceof Texture; 9 | } 10 | export function isHTMLCanvasElement(canvas): canvas is HTMLCanvasElement { 11 | return canvas instanceof HTMLCanvasElement; 12 | } 13 | export function isImageBitmap(imageBitmap): imageBitmap is ImageBitmap { 14 | return imageBitmap instanceof ImageBitmap; 15 | } 16 | export function isHTMLVideoElement(video): video is HTMLVideoElement { 17 | return video instanceof HTMLVideoElement; 18 | } 19 | // export function isVideoFrame(videoFrame): videoFrame is VideoFrame { 20 | // return videoFrame instanceof VideoFrame; 21 | // } 22 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import terser from "@rollup/plugin-terser"; 3 | import ts from "rollup-plugin-typescript2"; 4 | import commonjs from "@rollup/plugin-commonjs"; 5 | import { fileURLToPath } from "url"; 6 | import wgsl from "./build/@rollup/plugin-wgsl/index.js"; 7 | 8 | const { BUILD, BABEL } = process.env; 9 | const production = BUILD === "production"; 10 | const useBabel = BABEL === "true"; 11 | 12 | export default { 13 | input: "./src/index.ts", 14 | output: { 15 | file: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "./dist/index.js"), 16 | sourcemap: production ? false : "inline", 17 | format: "es", 18 | name: "index" 19 | }, 20 | watch: production 21 | ? false 22 | : { 23 | exclude: "node_modules/**" 24 | }, 25 | plugins: [ts(), wgsl(), commonjs({ ignoreGlobal: true }), production ? terser() : null] 26 | }; 27 | -------------------------------------------------------------------------------- /src/render/ComputePassEncoder.ts: -------------------------------------------------------------------------------- 1 | export class ComputePassEncoder { 2 | public device: GPUDevice; 3 | public commandEncoder: GPUCommandEncoder | null; 4 | private computeEncoder: GPUComputePassEncoder; 5 | constructor() { 6 | this.device = null; 7 | this.computeEncoder = null; 8 | } 9 | public beginComputePassEncoder(device: GPUDevice) { 10 | if (!this.device) this.device = device; 11 | this.commandEncoder = this.device.createCommandEncoder(); 12 | this.computeEncoder = this.commandEncoder.beginComputePass(); 13 | return this.computeEncoder; 14 | } 15 | public endComputePassEncoder() { 16 | this.computeEncoder?.end(); 17 | this.device.queue.submit([this.commandEncoder.finish()]); 18 | this.commandEncoder = null; 19 | } 20 | public destroy() { 21 | this.device = null; 22 | this.computeEncoder = null; 23 | this.commandEncoder = null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/ShaderChunk.ts: -------------------------------------------------------------------------------- 1 | import attributeChunks from "./attribute"; 2 | import commonChunks from "./common"; 3 | import environment from "./environment/environment"; // TODO 4 | import instanceChunks from "./instance"; 5 | import light from "./light/light.wgsl"; 6 | import lightCommon from "./light/lightCommon.wgsl"; 7 | import normalChunks from "./normal"; 8 | import pbrChunks from "./pbr"; 9 | import pbrFunction from "./pbr/pbrFunction"; 10 | import phongChunks from "./phong"; 11 | import skinChunks from "./skin"; 12 | import structChunks from "./struct"; 13 | 14 | const ShaderChunk = { 15 | ...attributeChunks, 16 | ...commonChunks, 17 | environment, 18 | ...instanceChunks, 19 | light, 20 | lightCommon, 21 | ...normalChunks, 22 | ...pbrChunks, 23 | pbrFunction, 24 | ...phongChunks, 25 | ...skinChunks, 26 | ...structChunks 27 | }; 28 | 29 | export default ShaderChunk; 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "declaration": false, 6 | // "declarationDir": "types", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "importHelpers": true, 10 | "lib": ["DOM", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "ES2021", "ES2022"], 11 | "module": "ES2020", 12 | "moduleResolution": "node", 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strictFunctionTypes": true, 16 | "target": "ES2020", 17 | "noEmit": false, 18 | // "declaration": true, 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src/**/*.ts", "src/shader/material/pbrFrag.ts"], 22 | "exclude": ["node_modules", "public", "dist", "example", "**/*.spec.ts"], 23 | "typeRoots": ["./node_modules/@webgpu/types/"] 24 | } 25 | -------------------------------------------------------------------------------- /src/light/HemisphereLightProbe.ts: -------------------------------------------------------------------------------- 1 | import Color from "../math/Color"; 2 | import Vector3 from "../math/Vector3"; 3 | import { LightProbe } from "./LightProbe"; 4 | 5 | export class HemisphereLightProbe extends LightProbe { 6 | constructor(skyColor, groundColor, intensity = 1) { 7 | super(undefined, intensity); 8 | const color1 = new Color().set(skyColor); 9 | const color2 = new Color().set(groundColor); 10 | 11 | const sky = new Vector3(color1.red, color1.green, color1.blue); 12 | const ground = new Vector3(color2.red, color2.green, color2.blue); 13 | 14 | // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); 15 | const c0 = Math.sqrt(Math.PI); 16 | const c1 = c0 * Math.sqrt(0.75); 17 | 18 | this.sh.coefficients[0].copy(sky).add(ground).multiplyByScalar(c0); 19 | this.sh.coefficients[1].copy(sky).subtract(ground).multiplyByScalar(c1); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/geometry/BoxGeometry.ts: -------------------------------------------------------------------------------- 1 | import { Float32Attribute } from "../render/Attribute"; 2 | import { createBox } from "../utils/GeometryUtils"; 3 | import Geometry from "./Geometry"; 4 | export default class BoxGeometry extends Geometry { 5 | constructor(public width: number = 10, public height: number = 10, public depth: number = 10) { 6 | super({ 7 | type: "boxGeometry" 8 | }); 9 | this.defines = { 10 | HAS_NORMAL: true 11 | }; 12 | this.init(); 13 | } 14 | private init() { 15 | // generate pos uv normal so on 16 | const { positions, normals, uvs } = createBox({ 17 | dimensions: [this.depth, this.width, this.height] 18 | }); 19 | this.computeBoundingSphere(positions); 20 | this.setAttribute(new Float32Attribute("position", positions, 3)); 21 | this.setAttribute(new Float32Attribute("normal", normals, 3)); 22 | this.setAttribute(new Float32Attribute("uv", uvs, 2)); 23 | this.count = 36; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/mesh/SkyBox.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { RenderObjectType } from "../core/WebGPUTypes"; 3 | import SkyBoxGeometry from "../geometry/SkyBoxGeometry"; 4 | import SkyBoxMaterial from "../material/SkyBoxMaterial"; 5 | import { Mesh } from "./Mesh"; 6 | export default class SkyBox extends Mesh { 7 | material: SkyBoxMaterial; 8 | public visibility: boolean; 9 | constructor(urls?: Array) { 10 | super(); 11 | this.type = RenderObjectType.Skybox; 12 | this.visibility = true; 13 | this.material = new SkyBoxMaterial(); 14 | if (urls) this.material.loadTexture(urls); 15 | this.geometry = new SkyBoxGeometry(); 16 | this.isSkyBox = true; 17 | } 18 | update(frameState: FrameState) { 19 | this.updateMatrix(); 20 | this.geometry.update({ frameState }); 21 | this.material.update(frameState, this); 22 | if (this.visibility) frameState.renderQueue.pre.push(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/render/Sampler.ts: -------------------------------------------------------------------------------- 1 | class Sampler { 2 | public gpuSampler: GPUSampler; 3 | public layoutType: GPUSamplerBindingLayout; 4 | 5 | static baseSampler = new Sampler({ 6 | magFilter: "linear", 7 | minFilter: "linear" 8 | }); 9 | 10 | constructor( 11 | public descriptor?: GPUSamplerDescriptor, 12 | layoutType: GPUSamplerBindingLayout = { 13 | type: "filtering" 14 | } 15 | ) { 16 | this.descriptor = {}; 17 | Object.assign( 18 | this.descriptor, 19 | { 20 | magFilter: "linear", 21 | minFilter: "linear", 22 | // mipmapFilter: "linear", 23 | addressModeU: "clamp-to-edge", 24 | addressModeV: "clamp-to-edge" 25 | // addressModeW: "clamp-to-edge", 26 | }, 27 | descriptor 28 | ); 29 | this.layoutType = layoutType; 30 | } 31 | update(device: GPUDevice) { 32 | if (!this.gpuSampler) this.gpuSampler = device.createSampler(this.descriptor); 33 | } 34 | } 35 | 36 | export default Sampler; 37 | -------------------------------------------------------------------------------- /src/mesh/Sprite.ts: -------------------------------------------------------------------------------- 1 | import { SpriteGeometry } from "../geometry/SpriteGeometry"; 2 | import { SpriteMaterial } from "../material/SpriteMaterial"; 3 | import Color from "../math/Color"; 4 | import Vector2 from "../math/Vector2"; 5 | import Sampler from "../render/Sampler"; 6 | import Texture from "../render/Texture"; 7 | import { Mesh } from "./Mesh"; 8 | 9 | export class Sprite extends Mesh { 10 | public rotation: number; 11 | public center: Vector2; 12 | public color: Color; 13 | public opacity: number; 14 | constructor() { 15 | super(); 16 | this.material = new SpriteMaterial(); 17 | this.geometry = new SpriteGeometry(); 18 | this.rotation = Math.PI; 19 | this.opacity = 1; 20 | this.center = new Vector2(0, 0); 21 | this.color = new Color(1, 0, 0); 22 | } 23 | setTexture(texture: Texture) { 24 | this.material.baseTexture = texture; 25 | } 26 | setSampler(sampler: Sampler) { 27 | this.material.baseSampler = sampler; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/shader/material/skyBoxFrag.wgsl: -------------------------------------------------------------------------------- 1 | 2 | fn lessThanEqual(a : vec3 , b : vec3 ) -> vec3 { 3 | let xValue : f32 = select(b.x, a.x, a.x <= b.x); 4 | let yValue : f32 = select(b.y, a.y, a.y <= b.y); 5 | let zValue : f32 = select(b.z, a.z, a.z <= b.z); 6 | return vec3 (xValue, yValue, zValue); 7 | } 8 | fn LinearTosRGB(value : vec4 ) -> vec4 { 9 | return vec4 (mix(pow(value.rgb, vec3 (0.41666) ) * 1.055 - vec3 (0.055), value.rgb * 12.92, vec3 (lessThanEqual(value.rgb, vec3 (0.0031308) )) ), value.a); 10 | } 11 | struct FragmentInput { 12 | @location(0) texCoord : vec3 13 | }; 14 | @group(0) @binding(2) var defaultSampler : sampler; 15 | @group(0) @binding(1) var skyboxTexture : texture_cube ; 16 | @fragment 17 | fn main(input : FragmentInput) -> @location(0) vec4 { 18 | let color = textureSample(skyboxTexture, defaultSampler, input.texCoord); 19 | return LinearTosRGB(color); 20 | } 21 | -------------------------------------------------------------------------------- /src/light/shadows/DirectionalLightShadow.ts: -------------------------------------------------------------------------------- 1 | import OrthographicCamera from "../../camera/OrthographicCamera"; 2 | import Vector2 from "../../math/Vector2"; 3 | import { DirectionalLight } from "../DirectionalLight"; 4 | import { Light } from "../Light"; 5 | import { BaseShadow } from "./BaseShadow"; 6 | 7 | export class DirectionalLightShadow extends BaseShadow { 8 | public type: string; 9 | constructor() { 10 | const camera = new OrthographicCamera(-50, 50, 50, -50, 0, 100); 11 | super(new Vector2(1024, 1024), camera); 12 | this.type = "directionalLightShadow"; 13 | super.init(); 14 | } 15 | 16 | public update(light: Light) { 17 | if (light instanceof DirectionalLight) this.updateMatrices(light); 18 | } 19 | 20 | updateMatrices(light: DirectionalLight) { 21 | this.camera.position.copy(light.position); 22 | const { x, y, z } = light.target; 23 | this.camera.lookAt(x, y, z); 24 | this.camera.updateMatrix(); 25 | this.vpMatrixDirty = true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/material/SpriteMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import { ShaderDataEnum } from "../core/WebGPUTypes"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | import { ShaderSource } from "../shader/ShaderSource"; 6 | import { Material } from "./Material"; 7 | 8 | export class SpriteMaterial extends Material { 9 | constructor() { 10 | super(); 11 | this.type = "sprite"; 12 | this.defines = { 13 | HAS_UV: true 14 | }; 15 | this.shaderSource = new ShaderSource({ 16 | shaderId: this.type, 17 | defines: this.defines 18 | }); 19 | } 20 | update(frameState?: FrameState, mesh?: Mesh) { 21 | if (!this.shaderData || this.dirty) 22 | this.shaderData = ShaderDataFactory.createShaderData({ 23 | mesh, 24 | material: this, 25 | shaderDataEnum: ShaderDataEnum.SPRITE 26 | }); 27 | } 28 | destroy() { 29 | this?.baseTexture?.destroy(); 30 | super.destroy(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/core/MeshManger.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { Mesh } from "../mesh/Mesh"; 3 | import { FrameState } from "./FrameState"; 4 | export default class MeshManger { 5 | private _list: Map; 6 | constructor() { 7 | this._list = new Map(); 8 | } 9 | get length() { 10 | return this._list.size; 11 | } 12 | update(frameState: FrameState, camera: Camera): void { 13 | this._list.forEach((instance) => { 14 | instance.update(frameState, camera); 15 | }); 16 | } 17 | add(instance: Mesh): Mesh { 18 | if (this._list.get(instance.uid)) return this._list.get(instance.uid); 19 | this._list.set(instance.uid, instance); 20 | return instance; 21 | } 22 | remove(instance: Mesh): boolean { 23 | if (this._list.get(instance.uid)) { 24 | instance.destroy(); 25 | this._list.delete(instance.uid); 26 | return true; 27 | } 28 | return false; 29 | } 30 | contains(instance: Mesh) { 31 | return !!this._list.get(instance.uid); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/shader/material/colorVert.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct VertexInput { 3 | @location(positionLocation) position : vec3 , 4 | @location(colorLocation) color : vec4 , 5 | } 6 | struct VertexOutput { 7 | @builtin(position) position : vec4 , 8 | @location(0) color : vec4 , 9 | }; 10 | struct SelfUniform { 11 | modelMatrix : mat4x4 , 12 | } 13 | struct SystemUniform { 14 | projectionMatrix : mat4x4 , 15 | viewMatrix : mat4x4 , 16 | inverseViewMatrix : mat4x4 , 17 | cameraPosition : vec3 , 18 | }; 19 | @binding(colorBinding) @group(0) var selfUniform : SelfUniform; 20 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 21 | @vertex 22 | fn main(input : VertexInput) -> VertexOutput { 23 | var output : VertexOutput; 24 | output.color = input.color; 25 | output.position = systemUniform.projectionMatrix * systemUniform.viewMatrix * selfUniform.modelMatrix * vec4 (input.position, 1.0); 26 | return output; 27 | } 28 | -------------------------------------------------------------------------------- /hooks/check-keyword.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | red=$(tput setaf 1) 3 | green=$(tput setaf 2) 4 | yellow=$(tput setaf 3) 5 | blue=$(tput setaf 4) 6 | reset=$(tput sgr0) 7 | # echo "${red}red text ${green}green text${reset}" 8 | echo "${green}check before committing...${reset}" 9 | for FILE in $(git diff --name-only --cached); do 10 | if [ ! -f $FILE ] || [[ $FILE == *".md"* ]] || [[ $FILE == *".html"* ]] || [[ $FILE == *"hooks"* ]] || [[ $FILE == *"dist"* ]] || [[ $FILE == *"example"* ]]; then 11 | echo "${blue}skip file " $FILE " => deleted or set not to check... ${reset}" 12 | continue 13 | fi 14 | 15 | # -e 实现多个选项间的逻辑or 关系 16 | # -i 查找时不区分大小写 17 | # -w 全字匹配 18 | if [ -f $FILE ]; then 19 | grep -n -i -w -w 'debugger' -w 'debugger;' -w 'console.log' -w 'console.log;' $FILE 2>&1 20 | fi 21 | 22 | if [ $? -eq 0 ]; then 23 | echo "${red}" $FILE "contains debugger/console.log, please delete it before submitting ${reset}" 24 | exit 1 25 | fi 26 | 27 | done 28 | echo "${green} check done ✅${reset}" 29 | exit 30 | -------------------------------------------------------------------------------- /src/render/PipelineLayout.ts: -------------------------------------------------------------------------------- 1 | import BindGroupLayout from "./BindGroupLayout"; 2 | 3 | const pipelineLayoutCache = new Map(); 4 | export class PipelineLayout { 5 | public gpuPipelineLayout: GPUPipelineLayout; 6 | index: number; 7 | private constructor(device: GPUDevice, label: string, public groupLayouts: BindGroupLayout[] = [], index?: number) { 8 | this.index = index || 0; 9 | this.gpuPipelineLayout = device.createPipelineLayout({ 10 | label: label, 11 | bindGroupLayouts: groupLayouts.map((layout) => { 12 | return layout.gpuBindGroupLayout; 13 | }) 14 | }); 15 | } 16 | static getPipelineLayoutFromCache( 17 | device: GPUDevice, 18 | label: string, 19 | groupLayouts: BindGroupLayout[] 20 | ): PipelineLayout { 21 | if (pipelineLayoutCache.has(label)) { 22 | return pipelineLayoutCache.get(label); 23 | } else { 24 | const bindGroupLayout = new PipelineLayout(device, label, groupLayouts); 25 | pipelineLayoutCache.set(label, bindGroupLayout); 26 | return bindGroupLayout; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/shader/shadow/shadowMapDebuggerFrag.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(1) var shadowSampler : sampler; 2 | @group(0) @binding(0) var shadowMap : texture_depth_2d; 3 | 4 | //@group(0) @binding(0) var shadowMap: texture_depth_2d_array; 5 | //@group(0) @binding(0) var shadowMap: texture_2d; 6 | 7 | struct VertexOutput { 8 | @builtin(position) position : vec4 , 9 | @location(0) uv : vec2 , 10 | }; 11 | 12 | fn linearizeDepth(depth : f32, near : f32, far : f32) -> f32 { 13 | return 2 * (near * far) / (far + near - depth * (far - near)); 14 | } 15 | 16 | @fragment 17 | fn main(input : VertexOutput) -> @location(0) vec4 { 18 | let color : vec4 = textureGather(shadowMap, shadowSampler, vec2 (input.uv.x, 1.0 - input.uv.y)); 19 | let depth = (linearizeDepth(color.r, 0.1, 500) - 0.1) / (500 - 0.1); 20 | return vec4(vec3(depth), 1.0); //PerspectiveCamera 21 | //return color; 22 | 23 | //return textureSample(shadowMap, shadowSampler, vec2(input.uv.x,1.0-input.uv.y)); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/loader/gltf/libs/Animation.ts: -------------------------------------------------------------------------------- 1 | import { Quaternion } from "../../../math/Quaternion"; 2 | import Vector4 from "../../../math/Vector4"; 3 | import { AnimationChannel } from "./AnimationChannel"; 4 | import { AnimationSampler } from "./AnimationSampler"; 5 | 6 | export class Animation { 7 | constructor(public name: string, public samplers: AnimationSampler[], public channels: AnimationChannel[]) {} 8 | play(time: number) { 9 | let node, animationSampler, target; 10 | this?.channels?.map((channel) => { 11 | animationSampler = channel.sampler; 12 | animationSampler.getValue(time); 13 | target = channel.target; 14 | node = target.node; 15 | switch (target.path) { 16 | case "rotation": 17 | Quaternion.clone(animationSampler.currentValue, node.quaternion); 18 | break; 19 | case "translation": 20 | Vector4.clone(animationSampler.currentValue, node.position); 21 | break; 22 | case "scale": 23 | Vector4.clone(animationSampler.currentValue, node.scale); 24 | break; 25 | } 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/material/PointMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import { ShaderDataEnum } from "../core/WebGPUTypes"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | import { ShaderSource } from "../shader/ShaderSource"; 6 | import { Material } from "./Material"; 7 | 8 | export class PointMaterial extends Material { 9 | constructor() { 10 | super(); 11 | this.type = "point"; 12 | this.defines = { 13 | HAS_UV: true, 14 | HAS_COLOR: true, 15 | VERTEX_COLOR: false, 16 | VERTEX_SIZE: false, 17 | USE_INSTANCE: true 18 | }; 19 | this.shaderSource = new ShaderSource({ 20 | shaderId: this.type, 21 | defines: this.defines 22 | }); 23 | } 24 | update(frameState?: FrameState, mesh?: Mesh) { 25 | if (!this.shaderData || this.dirty) 26 | this.shaderData = ShaderDataFactory.createShaderData({ 27 | mesh, 28 | material: this, 29 | shaderDataEnum: ShaderDataEnum.POINT 30 | }); 31 | } 32 | destroy() { 33 | this?.baseTexture?.destroy(); 34 | super.destroy(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/render/BindGroupLayoutEntry.ts: -------------------------------------------------------------------------------- 1 | import { BindGroupLayoutEntryType } from "../core/WebGPUTypes"; 2 | export default class BindGroupLayoutEntry { 3 | binding: number; 4 | visibility: GPUShaderStageFlags; 5 | uniforms?: any[]; 6 | buffer?: GPUBufferBindingLayout; 7 | sampler?: GPUSamplerBindingLayout; 8 | texture?: GPUTextureBindingLayout; 9 | storageTexture?: GPUStorageTextureBindingLayout; 10 | externalTexture?: GPUExternalTextureBindingLayout; 11 | constructor(options: BindGroupLayoutEntryType) { 12 | this.binding = options.binding; 13 | this.visibility = options.visibility; 14 | this.buffer = options.buffer; 15 | this.sampler = options.sampler; 16 | this.texture = options.texture; 17 | this.storageTexture = options.storageTexture; 18 | this.externalTexture = options.externalTexture; 19 | } 20 | getGPULayoutEntity() { 21 | return { 22 | binding: this.binding, 23 | visibility: this.visibility, 24 | buffer: this.buffer, 25 | sampler: this.sampler, 26 | texture: this.texture, 27 | externalTexture: this.externalTexture, 28 | storageTexture: this.storageTexture 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/math/Ray.ts: -------------------------------------------------------------------------------- 1 | import defined from "../utils/defined"; 2 | import Vector3 from "./Vector3"; 3 | 4 | /** 5 | * Represents a ray that extends infinitely from the provided origin in the provided direction. 6 | * @alias Ray 7 | * @constructor 8 | * 9 | * @param {Vector3} [origin=Vector3.ZERO] The origin of the ray. 10 | * @param {Vector3} [direction=Vector3.ZERO] The direction of the ray. 11 | */ 12 | export default class Ray { 13 | constructor(public origin: Vector3 = Vector3.ZERO, public direction: Vector3 = Vector3.ZERO) {} 14 | static clone(ray: Ray, result: Ray): Ray { 15 | if (!defined(ray)) { 16 | return undefined; 17 | } 18 | if (!defined(result)) { 19 | return new Ray(ray.origin, ray.direction); 20 | } 21 | result.origin = Vector3.clone(ray.origin); 22 | result.direction = Vector3.clone(ray.direction); 23 | return result; 24 | } 25 | 26 | static getPoint(ray: Ray, t: number, result: Vector3): Vector3 { 27 | if (!defined(result)) { 28 | result = new Vector3(); 29 | } 30 | 31 | result = Vector3.multiplyByScalar(ray.direction, t, result); 32 | return Vector3.add(ray.origin, result, result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/shader/material/skyBoxVert.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct SystemUniform { 3 | projectionMatrix : mat4x4 , 4 | viewMatrix : mat4x4 , 5 | inverseViewMatrix : mat4x4 , 6 | cameraPosition : vec3 , 7 | }; 8 | struct MaterialUniform { 9 | modelMatrix : mat4x4 , 10 | } 11 | @binding(skyboxBinding) @group(0) var selfUniform : MaterialUniform; 12 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 13 | struct VertexInput { 14 | @location(positionLocation) position : vec3 , 15 | }; 16 | struct VertexOutput { 17 | @builtin(position) position : vec4 , 18 | @location(0) texCoord : vec3 , 19 | }; 20 | @vertex 21 | fn main(input : VertexInput) -> VertexOutput { 22 | var output : VertexOutput; 23 | output.texCoord = input.position.xyz; 24 | var modelView = systemUniform.viewMatrix; 25 | //Drop the translation portion of the modelView matrix 26 | modelView[3] = vec4(0.0, 0.0, 0.0, modelView[3].w); 27 | output.position = systemUniform.projectionMatrix * modelView * vec4 (input.position, 1.0); 28 | output.position = output.position.xyww; 29 | return output; 30 | } 31 | -------------------------------------------------------------------------------- /src/shader/material/pbr_vs.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | @binding(pbrBinding) @group(0) var materialUniform : MaterialUniform; 8 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 9 | @vertex 10 | fn main(input : VertexInput) -> VertexOutput 11 | { 12 | var output : VertexOutput; 13 | #if HAS_UV 14 | output.uv = input.uv; 15 | #endif 16 | var modelMatrix : mat4x4 ; 17 | var vNormalView : vec3 ; 18 | vNormalView = normalize(materialUniform.normalMatrix * vec4 (input.normal, 0.0)).xyz; 19 | modelMatrix = materialUniform.modelMatrix; 20 | #include 21 | #include 22 | output.normal = vNormalView.xyz; 23 | output.position = systemUniform.projectionMatrix * systemUniform.viewMatrix * modelMatrix * vec4 (input.position, 1.0); 24 | let modelPos = modelMatrix * vec4 (input.position, 1.0); 25 | output.worldPos = modelPos.xyz / modelPos.w; 26 | return output; 27 | } 28 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/skin/getSkinMatrix.wgsl: -------------------------------------------------------------------------------- 1 | #if HAS_SKIN 2 | struct JointsUniform{ 3 | matrixs : array, 4 | } 5 | struct InverseBindMatricesUniform{ 6 | matrixs : array, 7 | } 8 | @binding(skinJointsBufferBinding) @group(0) var jointsUniform : JointsUniform; 9 | @binding(invsBufferBinding) @group(0) var inverseBindMatricesUniform : InverseBindMatricesUniform; 10 | fn getSkinMatrix(joints : vec4f, weights : vec4f) -> mat4x4 { 11 | let joint0 = jointsUniform.matrixs[u32(joints.x)] * inverseBindMatricesUniform.matrixs[u32(joints.x)]; 12 | let joint1 = jointsUniform.matrixs[u32(joints.y)] * inverseBindMatricesUniform.matrixs[u32(joints.y)]; 13 | let joint2 = jointsUniform.matrixs[u32(joints.z)] * inverseBindMatricesUniform.matrixs[u32(joints.z)]; 14 | let joint3 = jointsUniform.matrixs[u32(joints.w)] * inverseBindMatricesUniform.matrixs[u32(joints.w)]; 15 | 16 | let skinMatrix = joint0 * weights.x + 17 | joint1 * weights.y + 18 | joint2 * weights.z + 19 | joint3 * weights.w; 20 | return skinMatrix; 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/skin/skinVertHeader.wgsl: -------------------------------------------------------------------------------- 1 | #if HAS_SKIN 2 | struct JointsUniform{ 3 | matrixs : array, 4 | } 5 | struct InverseBindMatricesUniform{ 6 | matrixs : array, 7 | } 8 | @binding(skinJointsBufferBinding) @group(0) var jointsUniform : JointsUniform; 9 | @binding(invsBufferBinding) @group(0) var inverseBindMatricesUniform : InverseBindMatricesUniform; 10 | fn getSkinMatrix(joints : vec4f, weights : vec4f) -> mat4x4 { 11 | let joint0 = jointsUniform.matrixs[u32(joints.x)] * inverseBindMatricesUniform.matrixs[u32(joints.x)]; 12 | let joint1 = jointsUniform.matrixs[u32(joints.y)] * inverseBindMatricesUniform.matrixs[u32(joints.y)]; 13 | let joint2 = jointsUniform.matrixs[u32(joints.z)] * inverseBindMatricesUniform.matrixs[u32(joints.z)]; 14 | let joint3 = jointsUniform.matrixs[u32(joints.w)] * inverseBindMatricesUniform.matrixs[u32(joints.w)]; 15 | 16 | let skinMatrix = joint0 * weights.x + 17 | joint1 * weights.y + 18 | joint2 * weights.z + 19 | joint3 * weights.w; 20 | return skinMatrix; 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /src/shader/postProcess/bloom/LuminosityHigh.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct LuminosityUniforms{ 3 | luminosityThreshold : f32, 4 | smoothWidth : f32, 5 | defaultColor : vec3 , 6 | defaultOpacity : f32, 7 | } 8 | struct FragInput { 9 | @location(0) uv : vec2 , 10 | }; 11 | @group(0) @binding(0) var luminosityUniforms : LuminosityUniforms; 12 | @group(0) @binding({{tDiffuseBinding}}) var tDiffuse : texture_2d; 13 | @group(0) @binding({{tSamplerBinding}}) var tSampler : sampler; 14 | @fragment 15 | fn main(input : FragInput) -> @location(0) vec4 { 16 | 17 | let texel : vec4 = textureSample(tDiffuse, tSampler, input.uv); 18 | 19 | let luma : vec3 = vec3 (0.299, 0.587, 0.114); 20 | 21 | let v : f32 = dot(texel.xyz, luma); 22 | 23 | let outputColor : vec4 = vec4 (luminosityUniforms.defaultColor.rgb, luminosityUniforms.defaultOpacity); 24 | 25 | let alpha : f32 = smoothstep(luminosityUniforms.luminosityThreshold, luminosityUniforms.luminosityThreshold + luminosityUniforms.smoothWidth, v); 26 | 27 | return mix(outputColor, texel, alpha); 28 | } 29 | -------------------------------------------------------------------------------- /src/render/ComputeCommand.ts: -------------------------------------------------------------------------------- 1 | import { ShaderSource } from "../shader/ShaderSource"; 2 | import { Command } from "./Command"; 3 | import Pipeline from "./Pipeline"; 4 | import ShaderData from "./ShaderData"; 5 | 6 | export class ComputeCommand implements Command { 7 | public dispatch?: { x?: number; y?: number; z?: number }; 8 | public shaderSource?: ShaderSource; 9 | public shaderData?: ShaderData; 10 | constructor(options: ComputeCommandType) { 11 | this.dispatch = options.dispatch; 12 | this.shaderData = options.shaderData; 13 | this.shaderSource = options.shaderSource; 14 | } 15 | render(params?: ComputeParams): void { 16 | const { device, passEncoder } = params; 17 | this.shaderData?.bind?.(device, passEncoder); 18 | const pipeline = Pipeline.getComputePipelineFromCache(device, this, [this.shaderData.groupLayout]); 19 | pipeline.bind(passEncoder); 20 | const { x, y, z } = this.dispatch; 21 | passEncoder.dispatchWorkgroups(x, y, z); 22 | } 23 | } 24 | type ComputeCommandType = { 25 | dispatch?: { x?: number; y?: number; z?: number }; 26 | 27 | shaderSource?: ShaderSource; 28 | 29 | shaderData?: ShaderData; 30 | }; 31 | type ComputeParams = { 32 | device?: GPUDevice; 33 | passEncoder?: GPUComputePassEncoder; 34 | }; 35 | -------------------------------------------------------------------------------- /src/renderpipeline/ForwardRenderLine.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import { BasicPass } from "../pass/BasicPass"; 4 | import { ShadowPass } from "../pass/ShadowPass"; 5 | import Context from "../render/Context"; 6 | import Texture from "../render/Texture"; 7 | import IBaseRenderLine from "./IBaseRenderLine"; 8 | 9 | export default class ForwardRenderLine implements IBaseRenderLine { 10 | private basicPass: BasicPass; 11 | protected shadowPass: ShadowPass; 12 | constructor(public context: Context) { 13 | this.basicPass = new BasicPass(context); 14 | this.shadowPass = new ShadowPass(context); 15 | } 16 | getOutputTexture(): Texture { 17 | return this.basicPass.getColorTexture(0); 18 | // return this.shadowPass.getDepthTexture() 19 | } 20 | render(frameState: FrameState, camera?: Camera) { 21 | this.shadowPass.render(frameState, camera); 22 | 23 | this.basicPass.beforeRender(frameState); 24 | this.basicPass.render(frameState, camera); 25 | this.basicPass.afterRender(); 26 | } 27 | setSize(width: number, height: number) { 28 | this.basicPass.setSize(width, height); 29 | this.shadowPass.setSize(width, height); 30 | } 31 | destroy() { 32 | this.basicPass = undefined; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/loader/gltf/libs/Accessor.ts: -------------------------------------------------------------------------------- 1 | import Matrix4 from "../../../math/Matrix4"; 2 | import Vector4 from "../../../math/Vector4"; 3 | 4 | export class Accessor { 5 | values: any; 6 | id: number; 7 | count: number; 8 | componentType: number; 9 | type: number; 10 | min: number[]; 11 | max: number[]; 12 | constructor(options: AccessorParms) { 13 | this.values = options.values ?? []; 14 | this.id = options.id; 15 | this.count = options.count; 16 | this.componentType = options.componentType; 17 | this.type = options.type; 18 | this.min = options.min; 19 | this.max = options.max; 20 | } 21 | getArray(): number[] { 22 | return Array.from(this.values); 23 | } 24 | getVec4Array(): Vector4[] { 25 | const result = []; 26 | for (let i = 0; i < this.values.length; i += 4) { 27 | result.push(new Vector4(this.values[i], this.values[i + 1], this.values[i + 2], this.values[i + 3])); 28 | } 29 | return result; 30 | } 31 | getMat4Array(): Matrix4[] { 32 | const result = []; 33 | for (let i = 0; i < this.values.length; i += 16) { 34 | const mat4 = new Matrix4(); 35 | Matrix4.fromColumnMajorArray(this.values.slice(i, i + 16), mat4); 36 | result.push(mat4); 37 | } 38 | return result; 39 | } 40 | } 41 | export type AccessorParms = Partial; 42 | -------------------------------------------------------------------------------- /src/render/IndexBuffer.ts: -------------------------------------------------------------------------------- 1 | import { IndexFormat } from "../core/WebGPUConstant"; 2 | import { TypedArray } from "../utils/gltfUtils"; 3 | import Buffer from "./Buffer"; 4 | export default class IndexBuffer { 5 | buffer: Buffer; 6 | indices: TypedArray; 7 | indexFormat: GPUIndexFormat; 8 | dirty: boolean; 9 | private label: string; 10 | constructor(label: string, indices?: TypedArray) { 11 | this.label = label; 12 | this.indices = indices; 13 | this.indexFormat = indices instanceof Uint32Array ? IndexFormat.Uint32 : IndexFormat.Uint16; 14 | this.dirty = true; 15 | } 16 | setIndices(indices) { 17 | this.indices = indices; 18 | this.indexFormat = indices instanceof Uint32Array ? IndexFormat.Uint32 : IndexFormat.Uint16; 19 | this.dirty = true; 20 | } 21 | bind(device: GPUDevice, passEncoder: GPURenderPassEncoder) { 22 | if (this.dirty) { 23 | this.dirty = false; 24 | this.buffer = Buffer.createIndexBuffer( 25 | this.label, 26 | device, 27 | this.indices instanceof Array 28 | ? IndexFormat.Uint16 29 | ? new Uint16Array(this.indices) 30 | : new Uint32Array(this.indices) 31 | : this.indices 32 | ); 33 | } 34 | passEncoder.setIndexBuffer(this.buffer.gpuBuffer, this.indexFormat); 35 | } 36 | destroy() { 37 | this.buffer.destroy(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/mesh/Axes.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { RenderObjectType } from "../core/WebGPUTypes"; 3 | import Geometry from "../geometry/Geometry"; 4 | import ColorMaterial from "../material/ColorMaterial"; 5 | import { Float32Attribute } from "../render/Attribute"; 6 | import { Mesh } from "./Mesh"; 7 | export default class Axes extends Mesh { 8 | material: ColorMaterial; 9 | constructor() { 10 | super(); 11 | this.type = RenderObjectType.Axes; 12 | this.material = new ColorMaterial(); 13 | this.material.wireframe = true; 14 | this.init(); 15 | } 16 | get distanceToCamera(): number { 17 | return 20; 18 | } 19 | update(frameState: FrameState) { 20 | this.updateMatrix(); 21 | this.material.update(frameState, this); 22 | frameState.renderQueue.opaque.push(this); 23 | } 24 | private init() { 25 | const position = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]; 26 | const colors = [1, 0, 0, 1, 1, 0.5, 0.5, 1, 0, 1, 0, 1, 0.5, 1, 0.5, 1, 0, 0, 1, 1, 0.5, 0.5, 1, 1]; 27 | const indices = [0, 1, 2, 3, 4, 5]; 28 | this.geometry = new Geometry({}); 29 | this.geometry.setAttribute(new Float32Attribute("position", position, 3)); 30 | this.geometry.setAttribute(new Float32Attribute("color", colors, 4)); 31 | this.geometry.setIndice(indices); 32 | this.geometry.count = indices.length; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/render/BindGroupLayout.ts: -------------------------------------------------------------------------------- 1 | import BindGroupLayoutEntry from "./BindGroupLayoutEntry"; 2 | const layoutCache = new Map(); 3 | class BindGroupLayout { 4 | public gpuBindGroupLayout: GPUBindGroupLayout; 5 | index: number; 6 | public label: string; 7 | private constructor(device: GPUDevice, label: string, public entries: BindGroupLayoutEntry[] = [], index = 0) { 8 | this.index = index || 0; 9 | this.label = label; 10 | this.gpuBindGroupLayout = device.createBindGroupLayout({ 11 | label: label, 12 | entries: entries.map(({ visibility, buffer, sampler, texture, storageTexture, binding }) => ({ 13 | binding, 14 | visibility, 15 | buffer, 16 | sampler, 17 | texture, 18 | storageTexture 19 | })) 20 | }); 21 | } 22 | static getBindGroupLayoutFromCache( 23 | device: GPUDevice, 24 | label: string, 25 | entires: BindGroupLayoutEntry[], 26 | index 27 | ): BindGroupLayout { 28 | if (layoutCache.has(label)) { 29 | return layoutCache.get(label); 30 | } else { 31 | const bindGroupLayout = new BindGroupLayout(device, label, entires, index); 32 | layoutCache.set(label, bindGroupLayout); 33 | return bindGroupLayout; 34 | } 35 | } 36 | static removeBindGroupLayoutFromCache(bindGroupLayout: BindGroupLayout) { 37 | layoutCache.delete(bindGroupLayout); 38 | } 39 | } 40 | 41 | export default BindGroupLayout; 42 | -------------------------------------------------------------------------------- /src/geometry/PointGeometry.ts: -------------------------------------------------------------------------------- 1 | import { InputStepMode } from "../core/WebGPUConstant"; 2 | import { Attribute, InterleavedFloat32Attribute } from "../render/Attribute"; 3 | import VertexBuffer from "../render/VertexBuffer"; 4 | import Geometry from "./Geometry"; 5 | export default class PointGeometry extends Geometry { 6 | private instanceVertexBuffer: VertexBuffer; 7 | constructor() { 8 | super({ 9 | type: "pointGeometry" 10 | }); 11 | this.init(); 12 | } 13 | private init() { 14 | const indices = [0, 1, 2, 0, 2, 3]; 15 | const vertices = [-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]; 16 | this.computeBoundingSphere(vertices, 5); 17 | super.setAttribute(new InterleavedFloat32Attribute(["vertexPoint", "uv"], vertices, [3, 2])); 18 | this.setIndice(indices); 19 | this.count = indices.length; 20 | this.instanceVertexBuffer = new VertexBuffer({ 21 | label: this.type, 22 | index: this.vertexBufferCount, 23 | locationIndex: this.currentLocationIndex, 24 | stepMode: InputStepMode.Instance 25 | }); 26 | this.vertexBuffers.push(this.instanceVertexBuffer); 27 | } 28 | getAttribute(name: string) { 29 | return this.instanceVertexBuffer.getAttribute(name); 30 | } 31 | setAttribute(attribute: Attribute | InterleavedFloat32Attribute) { 32 | this.instanceVertexBuffer.setAttribute(attribute); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/light/PointLight.ts: -------------------------------------------------------------------------------- 1 | import { LightType } from "../core/WebGPUTypes"; 2 | import Vector3 from "../math/Vector3"; 3 | import { Light } from "./Light"; 4 | import { PointLightShadow } from "./shadows/PointLightShadow"; 5 | 6 | export class PointLight extends Light { 7 | private _distance: number; 8 | private _decay: number; 9 | distanceDirty: boolean; 10 | decayDirty: boolean; 11 | 12 | get shadow(): PointLightShadow { 13 | return this._shadow as PointLightShadow; 14 | } 15 | 16 | set shadow(value: PointLightShadow) { 17 | this.shadowDirty = true; 18 | this._shadow = value; 19 | } 20 | 21 | constructor(color: Vector3, intensity: number, distance = 0, decay = 4, openShadow = true) { 22 | super(color, intensity); 23 | this._distance = distance; 24 | this._decay = decay; 25 | this.distanceDirty = true; 26 | this.decayDirty = true; 27 | this.lightType = LightType.PointLight; 28 | if (openShadow) this.shadow = new PointLightShadow(); 29 | } 30 | set distance(value) { 31 | this.distanceDirty = true; 32 | this._distance = value; 33 | } 34 | get distance() { 35 | return this._distance; 36 | } 37 | set decay(value) { 38 | this.decayDirty = true; 39 | this._decay = value; 40 | } 41 | get decay() { 42 | return this._decay; 43 | } 44 | } 45 | // uniform 46 | // color: {}, 47 | // position: {}, 48 | // decay: {}, 49 | // distance: {} 50 | -------------------------------------------------------------------------------- /src/pass/RenderPass.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState.js"; 2 | import Context from "../render/Context.js"; 3 | import { Target } from "../render/RenderState.js"; 4 | import RenderTarget from "../render/RenderTarget.js"; 5 | import Texture from "../render/Texture.js"; 6 | 7 | class RenderPass { 8 | public renderTarget: RenderTarget; 9 | public context: Context; 10 | public colorTargets?: Array; 11 | public passRenderEncoder: GPURenderPassEncoder | null; 12 | constructor(context: Context) { 13 | this.context = context; 14 | } 15 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 16 | update(frameState: FrameState): void { 17 | // todo; 18 | } 19 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 20 | setSize(width: number, height: number): void { 21 | // todo ; 22 | } 23 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 24 | beforeRender(options?: any) { 25 | this.passRenderEncoder = this.renderTarget.beginRenderPass(this.context.device); 26 | } 27 | getColorTexture(index = 0): Texture { 28 | return this.renderTarget.getColorTexture(index) as Texture; 29 | } 30 | getDepthTexture(): Texture | { gpuTexture: GPUTexture } { 31 | return this.renderTarget.getDepthTexture(); 32 | } 33 | afterRender() { 34 | this.renderTarget.endRenderPass(); 35 | } 36 | } 37 | 38 | export default RenderPass; 39 | -------------------------------------------------------------------------------- /src/mesh/Points.ts: -------------------------------------------------------------------------------- 1 | import Geometry from "../geometry/Geometry"; 2 | import PointGeometry from "../geometry/PointGeometry"; 3 | import { Material } from "../material/Material"; 4 | import { Mesh } from "./Mesh"; 5 | 6 | export class Points extends Mesh { 7 | public size: number; 8 | private _vertexColor: boolean; 9 | private _vertexSize; 10 | constructor(geo: Geometry, mat: Material, pointCount = 1) { 11 | super(geo.type === "pointGeometry" ? geo : new PointGeometry(), mat); 12 | this.addAttributesToGeometry(geo); 13 | this.instanceCount = pointCount; 14 | this.frustumCull = false; 15 | this.size = 0.1; 16 | this._vertexColor = false; 17 | this._vertexSize = false; 18 | } 19 | get vertexColor(): boolean { 20 | return this._vertexColor; 21 | } 22 | set vertexColor(value: boolean) { 23 | this._vertexColor = value; 24 | this.material.shaderSource.setDefines({ VERTEX_COLOR: value }); 25 | } 26 | get vertexSize(): boolean { 27 | return this._vertexSize; 28 | } 29 | set vertextSize(value) { 30 | this._vertexSize = value; 31 | this.material.shaderSource.setDefines({ VERTEX_SIZE: value }); 32 | } 33 | private addAttributesToGeometry(geo: Geometry) { 34 | if (geo.type === "pointGeometry") return; 35 | const attributes = geo?.defaultVertexBuffer.attributes.values; 36 | attributes.forEach((attribute) => this.geometry.setAttribute(attribute)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/normal/getTBN.wgsl: -------------------------------------------------------------------------------- 1 | fn getTBN(input:FragInput)->mat3x3{ 2 | #if HAS_TANGENT 3 | let tbn:mat3x3 = input.tbn; 4 | #else 5 | let normal:vec3 =normalize(input.normal); 6 | let uv:vec2 = select(-input.uv,input.uv,input.frontFacing); 7 | // ref: http://www.thetenthplanet.de/archives/1180 8 | // get edge vectors of the pixel triangle 9 | let dp1:vec3 = vec3(dpdx(input.worldPos.x), dpdx(input.worldPos.y), dpdx(input.worldPos.z)); 10 | let dp2:vec3 = vec3(dpdy(input.worldPos.x), dpdy(input.worldPos.y), dpdy(input.worldPos.z)); 11 | let duv1:vec2 = dpdx(uv); 12 | let duv2:vec2 = dpdy(uv); 13 | 14 | // solve the linear system 15 | let dp2perp:vec3 = cross(dp2, normal); 16 | let dp1perp:vec3 = cross(normal, dp1); 17 | let tangent:vec3 = dp2perp * duv1.x + dp1perp * duv2.x; 18 | let binormal:vec3 = dp2perp * duv1.y + dp1perp * duv2.y; 19 | // construct a scale-invariant frame 20 | let result:f32=max(dot(tangent, tangent), dot(binormal, binormal)); 21 | let invmax:f32 = 1.0/sqrt(result); 22 | let tbn:mat3x3 = mat3x3(tangent * invmax, binormal * invmax, normal); 23 | #endif 24 | return tbn; 25 | } -------------------------------------------------------------------------------- /src/light/DirectionalLight.ts: -------------------------------------------------------------------------------- 1 | import { LightType } from "../core/WebGPUTypes"; 2 | import Vector3 from "../math/Vector3"; 3 | import { Scene } from "../Scene"; 4 | import { Light } from "./Light"; 5 | import { DirectionalLightCascadedShadow } from "./shadows/DirectionalLightCascadedShadow"; 6 | import { DirectionalLightShadow } from "./shadows/DirectionalLightShadow"; 7 | 8 | export class DirectionalLight extends Light { 9 | _scene: Scene; 10 | constructor(color: Vector3, intensity: number, openShadow = true, shadowOptions = { openCSM: true }) { 11 | super(color, intensity); 12 | this.lightType = LightType.DirectionalLight; 13 | if (openShadow) 14 | this.shadow = shadowOptions.openCSM 15 | ? new DirectionalLightCascadedShadow({ lightInstance: this }) 16 | : new DirectionalLightShadow(); 17 | } 18 | 19 | get dirtectDirty() { 20 | return this.positionDirty || this.targetDirty; 21 | } 22 | 23 | set dirtectDirty(value) { 24 | this.positionDirty = value; 25 | this.targetDirty = value; 26 | } 27 | 28 | get directional() { 29 | const result = new Vector3(); 30 | Vector3.subtract(this.target, this.position, result); 31 | return result.normalize(); 32 | } 33 | 34 | _setSceneInstance(scene: Scene) { 35 | this._scene = scene; 36 | } 37 | 38 | _getSceneActiveCamera() { 39 | return this._scene.camera; 40 | } 41 | } 42 | // uniform 43 | // direction: {}, 44 | // color: {} 45 | -------------------------------------------------------------------------------- /src/post-process/PostEffectCollection.ts: -------------------------------------------------------------------------------- 1 | import Context from "../render/Context"; 2 | import Texture from "../render/Texture"; 3 | import PostEffect from "./PostEffect"; 4 | import ResolvePostEffect from "./ResolvePostEffect"; 5 | 6 | export default class PostEffectCollection { 7 | private _postEffects: Map; 8 | public currentColorTexture: Texture; 9 | private resolvePostEffect: ResolvePostEffect; 10 | constructor() { 11 | this._postEffects = new Map(); 12 | this.currentColorTexture = undefined; 13 | this.resolvePostEffect = new ResolvePostEffect(); 14 | } 15 | add(postEffect: PostEffect) { 16 | this._postEffects.set(postEffect.id, postEffect); 17 | } 18 | remove(postEffect: PostEffect) { 19 | this._postEffects.delete(postEffect.id); 20 | postEffect.destroy(); 21 | } 22 | render(context: Context, colorTexture: Texture): void { 23 | this.currentColorTexture = colorTexture; 24 | this._postEffects.forEach((postEffect) => { 25 | this.currentColorTexture = postEffect.render(context, this.currentColorTexture); 26 | }); 27 | this.resolvePostEffect.render(context, this.currentColorTexture); 28 | } 29 | setSize(width: number, height: number) { 30 | this._postEffects.forEach((postEffect) => postEffect.setSize(width, height)); 31 | this.resolvePostEffect.setSize(width, height); 32 | } 33 | private postEffectsSort() { 34 | // this._postEffects.sort() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/material/ShaderMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import { ShaderDataEnum, ShaderMaterialParms, Uniforms } from "../core/WebGPUTypes"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | import { ShaderSource } from "../shader/ShaderSource"; 6 | import { Material } from "./Material"; 7 | 8 | export default class ShaderMaterial extends Material { 9 | uniforms: Uniforms; 10 | public shaderMaterialParms: ShaderMaterialParms; 11 | constructor(options: ShaderMaterialParms) { 12 | super(); 13 | const { type, frag, vert, defines = {}, light, shaderId } = options; 14 | this.defines = defines; 15 | this.type = shaderId ?? type; 16 | this.shaderMaterialParms = options; 17 | this.shaderSource = new ShaderSource({ 18 | shaderId: shaderId ?? type, 19 | render: { 20 | fragShader: frag, 21 | vertShader: vert 22 | }, 23 | defines: this.defines 24 | }); 25 | this.uniforms = options.uniforms; 26 | this.light = light || false; 27 | } 28 | update(frameState?: FrameState, mesh?: Mesh) { 29 | if (!this.shaderData || this.dirty) 30 | this.shaderData = ShaderDataFactory.createShaderData({ 31 | label: this.type, 32 | mesh, 33 | material: this, 34 | shaderDataEnum: ShaderDataEnum.CUSTOM 35 | }); 36 | } 37 | clone() { 38 | return new ShaderMaterial(this.shaderMaterialParms); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/geometry/SkyBoxGeometry.ts: -------------------------------------------------------------------------------- 1 | import { Float32Attribute } from "../render/Attribute"; 2 | import { GeometryUpdateParams } from "../type/UpdateParams"; 3 | import Geometry from "./Geometry"; 4 | export default class SkyBoxGeometry extends Geometry { 5 | constructor() { 6 | super({ 7 | type: "skyBoxGeometry" 8 | }); 9 | this.init(); 10 | } 11 | public init() { 12 | const positions = [ 13 | 1.0, 14 | 1.0, 15 | 1.0, // 0 16 | -1.0, 17 | 1.0, 18 | 1.0, // 1 19 | 1.0, 20 | -1.0, 21 | 1.0, // 2 22 | -1.0, 23 | -1.0, 24 | 1.0, // 3 25 | 1.0, 26 | 1.0, 27 | -1.0, // 4 28 | -1.0, 29 | 1.0, 30 | -1.0, // 5 31 | 1.0, 32 | -1.0, 33 | -1.0, // 6 34 | -1.0, 35 | -1.0, 36 | -1.0 // 7 37 | ]; 38 | const indices = [ 39 | // PosX (Right) 40 | 0, 2, 4, 6, 4, 2, 41 | 42 | // NegX (Left) 43 | 5, 3, 1, 3, 5, 7, 44 | 45 | // PosY (Top) 46 | 4, 1, 0, 1, 4, 5, 47 | 48 | // NegY (Bottom) 49 | 2, 3, 6, 7, 6, 3, 50 | 51 | // PosZ (Front) 52 | 0, 1, 2, 3, 2, 1, 53 | 54 | // NegZ (Back) 55 | 6, 5, 4, 5, 6, 7 56 | ]; 57 | this.setAttribute(new Float32Attribute("position", positions, 3)); 58 | this.setIndice(indices); 59 | this.count = indices.length; 60 | } 61 | // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars 62 | update(params?: GeometryUpdateParams): void {} 63 | } 64 | -------------------------------------------------------------------------------- /src/shader/material/point_vs.wgsl: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | struct SelfUniform { 6 | modelMatrix : mat4x4 , 7 | color : vec3 , 8 | size : f32, 9 | } 10 | @binding(pointBinding) @group(0) var selfUniform : SelfUniform; 11 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 12 | @vertex 13 | fn main(input : PointVertInput) -> PointVertOutput { 14 | var output : PointVertOutput; 15 | let v1=vec4(1.0,0.0,0.0,0.0); 16 | let v2=vec4(0.0,1.0,0.0,0.0); 17 | let v3=vec4(0.0,0.0,1.0,0.0); 18 | let rotatePoint=selfUniform.modelMatrix*vec4(input.position.x,input.position.y,input.position.z,1.0); 19 | let v4=vec4(rotatePoint.x,rotatePoint.y,rotatePoint.z,1.0); 20 | 21 | let matrix=mat4x4(v1,v2,v3,v4); 22 | var mvPosition : vec4 = systemUniform.viewMatrix *matrix* vec4 (0.0, 0.0, 0.0, 1.0); 23 | #if VERTEX_COLOR 24 | output.color = input.color; 25 | #endif 26 | #if VERTEX_SIZE 27 | let alignedPosition = input.vertexPoint.xy * input.size; 28 | #else 29 | let alignedPosition = input.vertexPoint.xy * selfUniform.size; 30 | #endif 31 | let newPoint = mvPosition.xy + alignedPosition; 32 | output.position = systemUniform.projectionMatrix * vec4 (newPoint.x, newPoint.y, mvPosition.z, mvPosition.w); 33 | return output; 34 | } 35 | -------------------------------------------------------------------------------- /src/shader/material/phongVert.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct MaterialUniform { 3 | modelMatrix : mat4x4 , 4 | color : vec3 , 5 | opacity : f32, 6 | normalMatrix : mat4x4 , 7 | emissive : vec3 , 8 | specular : vec3 , 9 | shininess : f32, 10 | } 11 | #include 12 | #include 13 | #include 14 | #include 15 | @binding(phongBinding) @group(0) var selfUniform : MaterialUniform; 16 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 17 | @vertex 18 | fn main(input : VertexInput) -> VertexOutput { 19 | var output : VertexOutput; 20 | #if HAS_UV 21 | output.uv = input.uv; 22 | #endif 23 | var modelMatrix:mat4x4; 24 | modelMatrix = selfUniform.modelMatrix; 25 | #include ; 26 | let modelPos = modelMatrix *vec4(input.position,1.0); 27 | output.worldPos = modelPos.xyz / modelPos.w; 28 | let vNormalView = selfUniform.normalMatrix * vec4 (input.normal, 0.0); 29 | output.normal = vNormalView.xyz; 30 | output.view = systemUniform.cameraPosition.xyz - modelPos.xyz; 31 | let viewPosition = systemUniform.viewMatrix * modelPos; 32 | output.viewPosition = -(viewPosition.xyz / viewPosition.w); 33 | output.position = systemUniform.projectionMatrix * systemUniform.viewMatrix * modelPos; 34 | return output; 35 | } 36 | -------------------------------------------------------------------------------- /src/material/SkyBoxMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import textureCache from "../core/TextureCache"; 4 | import { CompareFunction } from "../core/WebGPUConstant"; 5 | import { ShaderDataEnum } from "../core/WebGPUTypes"; 6 | import CubeTextureLoader from "../loader/CubeTextureLoader"; 7 | import { Mesh } from "../mesh/Mesh"; 8 | import { ShaderSource } from "../shader/ShaderSource"; 9 | import { Material } from "./Material"; 10 | export default class SkyBoxMaterial extends Material { 11 | loadFish: boolean; 12 | constructor() { 13 | super(); 14 | this.type = "skybox"; 15 | this.shaderSource = new ShaderSource({ 16 | shaderId: this.type 17 | }); 18 | this.loadFish = false; 19 | this.renderState.depthStencil.depthWriteEnabled = false; 20 | this.renderState.depthStencil.depthCompare = CompareFunction.LessEqual; 21 | } 22 | async loadTexture(urls) { 23 | const result = await CubeTextureLoader(urls); 24 | this.loadFish = true; 25 | textureCache.addTexture("specular", result.texture); 26 | this.baseTexture = result.texture; 27 | this.baseSampler = result.sampler; 28 | } 29 | update(frameState?: FrameState, mesh?: Mesh) { 30 | if (!this.loadFish) return; 31 | if (!this.shaderData) 32 | this.shaderData = ShaderDataFactory.createShaderData({ 33 | mesh, 34 | material: this, 35 | shaderDataEnum: ShaderDataEnum.SKYBOX 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/mesh/Node.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import RenderObject from "../core/RenderObject"; 4 | import { RenderObjectType } from "../core/WebGPUTypes"; 5 | import createGuid from "../utils/createGuid"; 6 | import { Mesh } from "./Mesh"; 7 | 8 | export default class Node extends RenderObject { 9 | uid: string; 10 | children: Map; 11 | name: string; 12 | constructor() { 13 | super(); 14 | this.type = RenderObjectType.Node; 15 | this.children = new Map(); 16 | this.parent = null; 17 | this.uid = createGuid(); 18 | } 19 | add(node: Node | Mesh) { 20 | node.parent = this; 21 | this.children.set(node.uid, node); 22 | } 23 | remove(node: Node | Mesh) { 24 | this.children.delete(node.uid); 25 | } 26 | update(frameState: FrameState, camera?: Camera) { 27 | this.updateMatrix(this?.parent?.modelMatrix?.clone()); 28 | this?.children?.forEach?.((node) => { 29 | node.update(frameState, camera); 30 | }); 31 | } 32 | destroy() { 33 | this.children.forEach((node) => { 34 | node.destroy(); 35 | }); 36 | this?.children?.clear(); 37 | } 38 | // eslint-disable-next-line @typescript-eslint/ban-types 39 | traverse(traverseFunction: Function, param: { [prop: string]: any }): void { 40 | for (let i = 0, len = this.children.size; i < len; i++) { 41 | this.children.forEach((child) => { 42 | child.traverse(traverseFunction, param); 43 | }); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/loader/CubeTextureLoader.ts: -------------------------------------------------------------------------------- 1 | import { TextureDimension, TextureViewDimension } from "../core/WebGPUConstant"; 2 | import Sampler from "../render/Sampler"; 3 | import Texture from "../render/Texture"; 4 | 5 | export default async function CubeTextureLoader(urls) { 6 | const promises = urls.map((src) => { 7 | const img = document.createElement("img"); 8 | img.src = src; 9 | return img.decode().then(() => createImageBitmap(img)); 10 | }); 11 | const images = await Promise.all(promises); 12 | await Promise.all(images); 13 | const baseSampler = new Sampler({ 14 | magFilter: "linear", 15 | minFilter: "linear" 16 | }); 17 | const data = images.map((image, i) => { 18 | return { 19 | source: image, 20 | width: image.width, 21 | height: image.height, 22 | depth: 1, 23 | x: 0, 24 | y: 0, 25 | z: i 26 | }; 27 | }); 28 | const baseTexture = new Texture({ 29 | label: "cubeTexture", 30 | generateMipmap: true, 31 | textureDescriptor: { 32 | dimension: TextureDimension.E2d, 33 | size: { 34 | width: images[0].width, 35 | height: images[0].height, 36 | depth: 6 37 | }, 38 | format: "rgba8unorm", 39 | usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, 40 | mipLevelCount: 6 41 | }, 42 | data, 43 | textureViewDescriptor: { 44 | dimension: TextureViewDimension.Cube, 45 | mipLevelCount: 6 46 | } 47 | }); 48 | return { 49 | texture: baseTexture, 50 | sampler: baseSampler 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/material/BlinnPhongMaterial.ts: -------------------------------------------------------------------------------- 1 | import { FrameState } from "../core/FrameState"; 2 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 3 | import { ShaderDataEnum } from "../core/WebGPUTypes"; 4 | import Color from "../math/Color"; 5 | import { Mesh } from "../mesh/Mesh"; 6 | import Sampler from "../render/Sampler"; 7 | import Texture from "../render/Texture"; 8 | import { ShaderSource } from "../shader/ShaderSource"; 9 | import { Material } from "./Material"; 10 | export default class BlinnPhongMaterial extends Material { 11 | public specular: Color; 12 | public shininess: number; 13 | public normalTexture: Texture; 14 | public normalSampler: Sampler; 15 | constructor() { 16 | super(); 17 | this.type = "phong"; 18 | this.color = new Color(1.0, 0.0, 0.0); 19 | this.defines = { 20 | materialPhong: true, 21 | MATERIAL_PHONG: true 22 | }; 23 | this.shaderSource = new ShaderSource({ 24 | shaderId: this.type, 25 | defines: this.defines 26 | }); 27 | this.light = true; 28 | this.specular = new Color(1.0, 1.0, 1.0); 29 | this.shininess = 30.0; 30 | this.baseTexture = undefined; 31 | this.baseSampler = undefined; 32 | } 33 | update(frameState?: FrameState, mesh?: Mesh) { 34 | if (!this.shaderData || this.dirty) 35 | this.shaderData = ShaderDataFactory.createShaderData({ 36 | mesh, 37 | material: this, 38 | shaderDataEnum: ShaderDataEnum.BLINNPHONG 39 | }); 40 | } 41 | destroy() { 42 | this?.baseTexture?.destroy(); 43 | super.destroy(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/core/FrameState.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import Color from "../math/Color"; 3 | import Context from "../render/Context"; 4 | import Texture from "../render/Texture"; 5 | import { Scene } from "../Scene"; 6 | import { Frustum } from "./Frustum"; 7 | import LightManger from "./LightManger"; 8 | import RenderQueue from "./RenderQueue"; 9 | 10 | interface FrameStateOptions { 11 | background?: Background; 12 | } 13 | 14 | export interface Background { 15 | value: Color | Texture; 16 | opacity: number; 17 | } 18 | 19 | export class FrameState { 20 | public background: Background; 21 | public renderQueue: RenderQueue; 22 | public drawCallnums: number; 23 | public geometryMemory: number; 24 | public textureMemory: number; 25 | public frameNumber: number; 26 | public frustum: Frustum; 27 | constructor(public context: Context, public lightManger?: LightManger, options: FrameStateOptions = {}) { 28 | this.background = options.background; 29 | this.renderQueue = new RenderQueue(); 30 | this.geometryMemory = 0; 31 | this.textureMemory = 0; 32 | this.frameNumber = 0; 33 | this.frustum = new Frustum(); 34 | } 35 | update(camera: Camera, options: FrameStateOptions = {}) { 36 | this.background = options.background; 37 | 38 | this.renderQueue.reset(); 39 | this?.lightManger?.update?.(); 40 | this.frustum.update(camera); 41 | this.frameNumber += 1; 42 | } 43 | static getFrameStateOptionsByScene(sceneInstance: Scene) { 44 | return { 45 | background: sceneInstance.background 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/common/TextureAndSamplerDefine.wgsl: -------------------------------------------------------------------------------- 1 | #if USE_IBL 2 | @group(0) @binding(specularEnvTextureBinding) var specularEnvTexture : texture_cube ; 3 | @group(0) @binding(specularEnvSamplerBinding) var specularEnvSampler : sampler; 4 | #endif 5 | #if USE_BASECOLORTEXTURE 6 | @group(0) @binding(baseColorTextureBinding) var baseColorTexture : texture_2d; 7 | @group(0) @binding(baseColorSamplerBinding) var baseColorSampler : sampler; 8 | #endif 9 | //normal map 10 | #if USE_NORMALTEXTURE 11 | @group(0) @binding(normalTextureBinding) var normalTexture : texture_2d; 12 | @group(0) @binding(normalSamplerBinding) var normalSampler : sampler; 13 | #endif 14 | //emmisve map 15 | #if USE_EMISSIVETEXTURE 16 | @group(0) @binding(emissiveTextureBinding) var emissiveTexture : texture_2d; 17 | @group(0) @binding(emissiveSamplerBinding) var emissiveSampler : sampler; 18 | #endif 19 | 20 | //metal roughness 21 | #if USE_METALNESSROUGHNESSTEXTURE 22 | @group(0) @binding(metalnessRoughnessTextureBinding) var metalnessRoughnessTexture : texture_2d; 23 | @group(0) @binding(metalnessRoughnessSamplerBinding) var metalnessRoughnessSampler : sampler; 24 | #endif 25 | //occlusion texture 26 | #if USE_AOTEXTURE 27 | @group(0) @binding(aoTextureBinding) var aoTexture : texture_2d; 28 | @group(0) @binding(aoSamplerBinding) var aoSampler : sampler; 29 | #endif 30 | #if USE_NORMALTEXTURE 31 | #include 32 | #include 33 | #else 34 | #include 35 | #endif 36 | -------------------------------------------------------------------------------- /src/math/Spherical.ts: -------------------------------------------------------------------------------- 1 | import GMath from "./Math.js"; 2 | import Vector3 from "./Vector3.js"; 3 | // from three.js 4 | class Spherical { 5 | radius: number; 6 | phi: number; 7 | theta: number; 8 | 9 | constructor(radius = 1, phi = 0, theta = 0) { 10 | this.radius = radius; 11 | this.phi = phi; // polar angle 12 | this.theta = theta; // azimuthal angle 13 | 14 | return this; 15 | } 16 | set(radius: number, phi: number, theta: number): Spherical { 17 | this.radius = radius; 18 | this.phi = phi; 19 | this.theta = theta; 20 | 21 | return this; 22 | } 23 | copy(other: Spherical): Spherical { 24 | this.radius = other.radius; 25 | this.phi = other.phi; 26 | this.theta = other.theta; 27 | 28 | return this; 29 | } 30 | // restrict phi to be between EPS and PI-EPS 31 | makeSafe(): Spherical { 32 | const EPS = 0.000001; 33 | this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); 34 | 35 | return this; 36 | } 37 | 38 | setFromVector3(v: Vector3): Spherical { 39 | return this.setFromCartesianCoords(v.x, v.y, v.z); 40 | } 41 | setFromCartesianCoords(x: number, y: number, z: number): Spherical { 42 | this.radius = Math.sqrt(x * x + y * y + z * z); 43 | 44 | if (this.radius === 0) { 45 | this.theta = 0; 46 | this.phi = 0; 47 | } else { 48 | this.theta = Math.atan2(x, z); 49 | this.phi = Math.acos(GMath.clamp(y / this.radius, -1, 1)); 50 | } 51 | 52 | return this; 53 | } 54 | 55 | clone(): Spherical { 56 | return new Spherical(this.radius, this.phi, this.theta); 57 | } 58 | } 59 | 60 | export { Spherical }; 61 | -------------------------------------------------------------------------------- /src/shader/shadow/shadowMapVert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | #if USE_INSTANCE 3 | @builtin(instance_index) instanceIdx : u32, 4 | #endif 5 | @location(positionLocation) position : vec3 , 6 | }; 7 | struct VertexOutput { 8 | @builtin(position) position : vec4 , 9 | }; 10 | struct SelfUniform { 11 | modelMatrix : mat4x4 , 12 | }; 13 | struct SystemUniform { 14 | projectionMatrix : mat4x4 , 15 | viewMatrix : mat4x4 , 16 | inverseViewMatrix : mat4x4 , 17 | cameraPosition : vec3 , 18 | }; 19 | #include 20 | 21 | #if IS_POINTLIGHT_SHADOWMAP 22 | struct PointLightUniform { 23 | vpMatrix : mat4x4 , 24 | //vpMatrixArray: array, 6>, 25 | }; 26 | @group(1) @binding(pointLightShadowCameraBinding) var pointLightUniform : PointLightUniform; 27 | #endif 28 | 29 | @group(0) @binding(selfBinding) var selfUniform : SelfUniform; 30 | @group(1) @binding(cameraBinding) var systemUniform : SystemUniform; 31 | 32 | @vertex 33 | fn main(input : VertexInput) -> VertexOutput { 34 | var output : VertexOutput; 35 | var modelMatrix:mat4x4; 36 | modelMatrix = selfUniform.modelMatrix; 37 | #include 38 | #if IS_POINTLIGHT_SHADOWMAP 39 | output.position = pointLightUniform.vpMatrix * modelMatrix * vec4(input.position,1.0); 40 | #else 41 | output.position = systemUniform.projectionMatrix * systemUniform.viewMatrix * modelMatrix * vec4(input.position,1.0); 42 | #endif 43 | return output; 44 | } 45 | -------------------------------------------------------------------------------- /src/shader/postProcess/bloom/Blur.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct FragInput { 3 | @location(0) uv: vec2, 4 | } 5 | struct BlurUniforms { 6 | direction:vec2, 7 | } 8 | fn gaussianPdf(x:f32, sigma:f32)->f32 { 9 | return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma; 10 | } 11 | @group(0) @binding(0) var blurUniforms : BlurUniforms; 12 | @group(0) @binding({{tDiffuseBinding}}) var tDiffuse: texture_2d; 13 | @group(0) @binding({{tSamplerBinding}}) var tSampler: sampler; 14 | @fragment 15 | fn main(input:FragInput) -> @location(0) vec4 { 16 | let invSize:vec2 = vec2(1.0,1.0) / vec2(textureDimensions(tDiffuse)); 17 | let fSigma:f32 =f32(sigmaConst); 18 | var weightSum:f32 = gaussianPdf(0.0, fSigma); 19 | let baseColor=textureSample(tDiffuse, tSampler, input.uv); 20 | var diffuseSum:vec3 = baseColor.rgb * weightSum; 21 | let uvOffset:vec2 = blurUniforms.direction * invSize; 22 | for( var i : u32 = 1; i < kernelRadius;i = i + 1 ) { 23 | let x:f32 = f32(i); 24 | let w:f32 = gaussianPdf(x, fSigma); 25 | let sample1:vec3=textureSample(tDiffuse, tSampler, input.uv+ uvOffset*x).rgb; 26 | let sample2:vec3=textureSample(tDiffuse, tSampler, input.uv- uvOffset*x).rgb; 27 | diffuseSum =diffuseSum+ (sample2+sample2)* w; 28 | weightSum += 2.0 * w; 29 | } 30 | diffuseSum/=weightSum; 31 | return vec4(diffuseSum,baseColor.a); 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/core/EventDispatcher.ts: -------------------------------------------------------------------------------- 1 | export class EventDispatcher { 2 | private _listeners: object; 3 | constructor() { 4 | this._listeners = {}; 5 | } 6 | 7 | addEventListener(type, listener) { 8 | if (this._listeners === undefined) this._listeners = {}; 9 | 10 | const listeners = this._listeners; 11 | 12 | if (listeners[type] === undefined) { 13 | listeners[type] = []; 14 | } 15 | 16 | if (listeners[type].indexOf(listener) === -1) { 17 | listeners[type].push(listener); 18 | } 19 | } 20 | 21 | hasEventListener(type, listener) { 22 | if (this._listeners === undefined) return false; 23 | 24 | const listeners = this._listeners; 25 | 26 | return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; 27 | } 28 | 29 | removeEventListener(type, listener) { 30 | if (this._listeners === undefined) return; 31 | 32 | const listeners = this._listeners; 33 | const listenerArray = listeners[type]; 34 | 35 | if (listenerArray !== undefined) { 36 | const index = listenerArray.indexOf(listener); 37 | 38 | if (index !== -1) { 39 | listenerArray.splice(index, 1); 40 | } 41 | } 42 | } 43 | 44 | dispatchEvent(event) { 45 | if (this._listeners === undefined) return; 46 | 47 | const listeners = this._listeners; 48 | const listenerArray = listeners[event.type]; 49 | 50 | if (listenerArray !== undefined) { 51 | event.target = this; 52 | 53 | const array = listenerArray.slice(0); 54 | 55 | for (let i = 0, l = array.length; i < l; i++) { 56 | array[i].call(this, event); 57 | } 58 | event.target = null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:import/recommended", 12 | "plugin:import/typescript", 13 | "plugin:prettier/recommended" 14 | ], 15 | "parser": "@typescript-eslint/parser", 16 | "parserOptions": { 17 | "parser": "@typescript-eslint/parser", 18 | "sourceType": "module" 19 | }, 20 | "plugins": ["unused-imports"], 21 | "rules": { 22 | "import/no-named-as-default": "off", 23 | "vue/no-deprecated-v-on-native-modifier": "off", 24 | "vue/multi-word-component-names": "off", 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "@typescript-eslint/ban-ts-comment": "off", 27 | "unused-imports/no-unused-imports": "warn", 28 | "spaced-comment": [ 29 | "error", 30 | "always", 31 | { 32 | "block": { 33 | "markers": ["!"], 34 | "exceptions": ["*"], 35 | "balanced": true 36 | } 37 | } 38 | ], 39 | "no-console": ["error", { "allow": ["warn", "error"] }], 40 | "no-debugger": "error", 41 | "@typescript-eslint/no-empty-interface": [ 42 | "error", 43 | { 44 | "allowSingleExtends": true 45 | } 46 | ], 47 | "prettier/prettier": ["error", { "endOfLine": "auto" }], 48 | "import/no-unresolved": "off", 49 | "import/order": [ 50 | "warn", 51 | { 52 | "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"], 53 | "alphabetize": { 54 | "order": "asc", 55 | "caseInsensitive": true 56 | } 57 | } 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/lib/esm/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common utilities 3 | * @module glMatrix 4 | */ 5 | // Configuration Constants 6 | export var EPSILON = 0.000001; 7 | export var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; 8 | export var RANDOM = Math.random; 9 | export var ANGLE_ORDER = "zyx"; 10 | /** 11 | * Sets the type of array used when creating new vectors and matrices 12 | * 13 | * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array 14 | */ 15 | 16 | export function setMatrixArrayType(type) { 17 | ARRAY_TYPE = type; 18 | } 19 | var degree = Math.PI / 180; 20 | /** 21 | * Convert Degree To Radian 22 | * 23 | * @param {Number} a Angle in Degrees 24 | */ 25 | 26 | export function toRadian(a) { 27 | return a * degree; 28 | } 29 | /** 30 | * Tests whether or not the arguments have approximately the same value, within an absolute 31 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less 32 | * than or equal to 1.0, and a relative tolerance is used for larger values) 33 | * 34 | * @param {Number} a The first number to test. 35 | * @param {Number} b The second number to test. 36 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise. 37 | */ 38 | 39 | export function equals(a, b) { 40 | return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); 41 | } 42 | if (!Math.hypot) 43 | Math.hypot = function () { 44 | var y = 0, 45 | i = arguments.length; 46 | 47 | while (i--) { 48 | y += arguments[i] * arguments[i]; 49 | } 50 | 51 | return Math.sqrt(y); 52 | }; 53 | -------------------------------------------------------------------------------- /src/shader/material/sprite_vs.wgsl: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | struct SelfUniform { 6 | modelMatrix : mat4x4 , 7 | color : vec3 , 8 | rotation : f32, 9 | center : vec2 , 10 | opacity : f32, 11 | } 12 | @binding(spriteBinding) @group(0) var selfUniform : SelfUniform; 13 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 14 | @vertex 15 | fn main(input : VertexInput) -> VertexOutput { 16 | var output : VertexOutput; 17 | var mvPosition : vec4 = systemUniform.viewMatrix * selfUniform.modelMatrix * vec4 (0.0, 0.0, 0.0, 1.0); 18 | #if HAS_UV 19 | output.uv = input.uv; 20 | #endif 21 | var scale : vec2 ; 22 | scale.x = length(vec3 (selfUniform.modelMatrix[0].x, selfUniform.modelMatrix[0].y, selfUniform.modelMatrix[0].z)); 23 | scale.y = length(vec3 (selfUniform.modelMatrix[1].x, selfUniform.modelMatrix[1].y, selfUniform.modelMatrix[1].z)); 24 | //scale *= - mvPosition.z; 25 | var alignedPosition : vec2 =(input.position.xy - (selfUniform.center - vec2 (0.5, 0.5))) * scale; 26 | let rotatedPositionX = cos(selfUniform.rotation) * alignedPosition.x - sin(selfUniform.rotation) * alignedPosition.y; 27 | let rotatedPositionY = sin(selfUniform.rotation) * alignedPosition.x + cos(selfUniform.rotation) * alignedPosition.y; 28 | var rotatedPosition = vec2 (rotatedPositionX, rotatedPositionY); 29 | let newPoint = mvPosition.xy + rotatedPosition; 30 | output.position = systemUniform.projectionMatrix * vec4 (newPoint.x, newPoint.y, mvPosition.z, mvPosition.w); 31 | return output; 32 | } 33 | -------------------------------------------------------------------------------- /src/camera/PerspectiveCamera.ts: -------------------------------------------------------------------------------- 1 | import GMath from "../math/Math"; 2 | import Matrix4 from "../math/Matrix4"; 3 | import Camera from "./Camera"; 4 | export default class PerspectiveCamera extends Camera { 5 | xOffset: number; 6 | yOffset: number; 7 | private _aspect: number; 8 | private _fov: number; 9 | height: number; 10 | width: number; 11 | isPerspectiveCamera: boolean; 12 | top: number; 13 | left: number; 14 | constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { 15 | super(); 16 | this._aspect = aspect; 17 | this.fov = fov; 18 | this.near = near; 19 | this.far = far; 20 | this.xOffset = 0; 21 | this.yOffset = 0; 22 | this.projectMatrixDirty = true; 23 | this.updateCameraParms(); 24 | this.isPerspectiveCamera = true; 25 | } 26 | get aspect(): number { 27 | return this._aspect; 28 | } 29 | 30 | set aspect(v: number) { 31 | this.projectMatrixDirty = true; 32 | this._aspect = v; 33 | } 34 | get fov(): number { 35 | return this._fov; 36 | } 37 | 38 | set fov(v: number) { 39 | this.projectMatrixDirty = true; 40 | this._fov = v; 41 | } 42 | private updateCameraParms() { 43 | this.top = this.near * Math.tan(0.5 * GMath.RADIANS_PER_DEGREE * this.fov); 44 | this.height = 2 * this.top; 45 | this.width = this.aspect * this.height; 46 | this.left = -0.5 * this.width; 47 | } 48 | public updateProjectionMatrix() { 49 | if (this.projectMatrixDirty) { 50 | this.updateCameraParms(); 51 | this._projectionMatrix = Matrix4.makePerspective( 52 | this.left, 53 | this.left + this.width, 54 | this.top, 55 | this.top - this.height, 56 | this.near, 57 | this.far 58 | ); 59 | this.projectMatrixDirty = false; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/geometry/SphereGeometry.ts: -------------------------------------------------------------------------------- 1 | import { Attribute, Float32Attribute } from "../render/Attribute"; 2 | import { createSphere } from "../utils/GeometryUtils"; 3 | import Geometry from "./Geometry"; 4 | 5 | export default class SphereGeometry extends Geometry { 6 | radius: number; 7 | constructor(radius: number) { 8 | super({ 9 | type: "sphereGeometry" 10 | }); 11 | this.defines = { 12 | HAS_NORMAL: true 13 | }; 14 | this.radius = radius; 15 | this.init(); 16 | } 17 | private init() { 18 | const { positions, normals, uvs, indices } = createSphere({ radius: this.radius }); 19 | // this.positions = positions; 20 | // this.normals = normals; 21 | // this.uvs = uvs; 22 | // this.indices = indices; 23 | this.computeBoundingSphere(positions); 24 | this.setAttribute(new Float32Attribute("position", positions, 3)); 25 | this.setAttribute(new Float32Attribute("normal", normals, 3)); 26 | this.setAttribute(new Float32Attribute("uv", uvs, 2)); 27 | 28 | this.setIndice(indices); 29 | this.count = indices.length; 30 | } 31 | 32 | private updateAttribute() { 33 | const { positions, normals, uvs, indices } = createSphere({ radius: this.radius }); 34 | this.computeBoundingSphere(positions); 35 | const newPositionAtt = new Float32Attribute("position", positions, 3); 36 | const newNormalAtt = new Float32Attribute("normal", normals, 3); 37 | const newUvAtt = new Float32Attribute("uv", uvs, 2); 38 | (this.getAttribute("position") as Attribute).setValue(newPositionAtt.value); 39 | (this.getAttribute("normal") as Attribute).setValue(newNormalAtt.value); 40 | (this.getAttribute("uv") as Attribute).setValue(newUvAtt.value); 41 | this.setIndice(indices); 42 | this.count = indices.length; 43 | } 44 | 45 | updateGeometry(radius: number) { 46 | this.radius = radius; 47 | this.updateAttribute(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/render/Attachment.ts: -------------------------------------------------------------------------------- 1 | import { AttachmentOptions } from "../core/WebGPUTypes"; 2 | import Texture from "./Texture"; 3 | 4 | class Attachment { 5 | public op: GPULoadOp = "clear"; 6 | public storeOp: GPUStoreOp = "store"; 7 | 8 | public texture?: Texture; 9 | public resolveTarget?: Texture; 10 | public textureView?: () => GPUTextureView | GPUTextureView; 11 | public readOnly?: boolean; 12 | 13 | constructor(public value: GPUColorDict | GPUColor | number, options?: AttachmentOptions) { 14 | Object.assign(this, options); 15 | } 16 | getGPURenderPassColorAttachment(device: GPUDevice): GPURenderPassColorAttachment { 17 | this?.texture?.update?.(device); 18 | return { 19 | view: 20 | // 暂时这么写 21 | (this?.textureView?.() || this?.textureView) ?? this.texture.textureView, 22 | resolveTarget: this?.resolveTarget?.textureView, 23 | clearValue: this.value, 24 | loadOp: this.op, 25 | storeOp: this.storeOp 26 | } as GPURenderPassColorAttachment; 27 | } 28 | getGPURenderPassDepthAttachment(device: GPUDevice): GPURenderPassDepthStencilAttachment { 29 | this?.texture?.update?.(device); 30 | return { 31 | view: this?.texture?.textureView || undefined, 32 | depthLoadOp: this?.op || "clear", 33 | depthClearValue: this?.value || 1.0, 34 | depthStoreOp: this?.storeOp || "store", 35 | depthReadOnly: this?.readOnly || false 36 | } as GPURenderPassDepthStencilAttachment; 37 | } 38 | getGPURenderPassStencilAttachment(device: GPUDevice): GPURenderPassDepthStencilAttachment { 39 | this?.texture?.update?.(device); 40 | return { 41 | stencilLoadOp: this?.op || "clear", 42 | stencilClearValue: this?.value || 0, 43 | stencilStoreOp: this?.storeOp || "store", 44 | stencilReadOnly: this?.readOnly || false 45 | } as GPURenderPassDepthStencilAttachment; 46 | } 47 | } 48 | 49 | export default Attachment; 50 | -------------------------------------------------------------------------------- /src/shader/README.md: -------------------------------------------------------------------------------- 1 | ## Internal constants are mixed in lowercase and uppercase, remember not to use all uppercase 2 | 3 | e.g. : 4 | 5 | ``` 6 | struct VertexInput { 7 | @location(positionLocation) position: vec3, 8 | @location(colorLocation) color: vec4, 9 | } 10 | // Both positionLocation and colorLocation are filled with constants, then the defines are defined as 11 | const defines={ 12 | positionLocation:0, 13 | colorLocation:1 14 | } 15 | ``` 16 | 17 | ## Non-internal constants, all uppercase/all uppercase+underscore+number 18 | 19 | e.g.: 20 | 21 | ``` 22 | #if USE_IBL 23 | var reflectedLightDiffuse=indirectDiffuse_Physical(geometry,material); 24 | var reflectedLightSpecular=indirectSpecular_Physical(geometry,material); 25 | color+=reflectedLightDiffuse.indirectDiffuse; 26 | color+=reflectedLightSpecular.indirectSpecular; 27 | #endif 28 | ``` 29 | 30 | USE_IBL is defined for shader override macros 31 | 32 | ``` 33 | const defines={ 34 | USE_IBL:true 35 | } 36 | ``` 37 | 38 | ## Note 39 | 40 | Macro definitions do not support expressions, and do not support cross operators ((USE_IBL &&HAS_UV)||(USE_INSTANCE||USENORMAL) is an error) 41 | Only identical operators are supported, e.g.: 42 | 43 | ``` 44 | #if USE_IBL||HAS_UV||USE_INSTANCE 45 | var reflectedLightDiffuse=indirectDiffuse_Physical(geometry,material); 46 | var reflectedLightSpecular=indirectSpecular_Physical(geometry,material); 47 | color+=reflectedLightDiffuse.indirectDiffuse; 48 | color+=reflectedLightSpecular.indirectSpecular; 49 | #endif 50 | #if USE_IBL&&HAS_UV&&USE_INSTANCE 51 | var reflectedLightDiffuse=indirectDiffuse_Physical(geometry,material); 52 | var reflectedLightSpecular=indirectSpecular_Physical(geometry,material); 53 | color+=reflectedLightDiffuse.indirectDiffuse; 54 | color+=reflectedLightSpecular.indirectSpecular; 55 | #endif 56 | ``` 57 | -------------------------------------------------------------------------------- /src/light/shadows/DirectionalLightCascadedShadow.ts: -------------------------------------------------------------------------------- 1 | import Matrix4 from "../../math/Matrix4"; 2 | import Vector2 from "../../math/Vector2"; 3 | import { DirectionalLight } from "../DirectionalLight"; 4 | import { Light } from "../Light"; 5 | import { CascadedShadow, defaultCascadedShadowOptions } from "./CascadedShadow"; 6 | 7 | export class DirectionalLightCascadedShadow extends CascadedShadow { 8 | public type: string; 9 | isCascadedShadow: boolean; 10 | vpMatrixArrayDirty: boolean; 11 | 12 | constructor(options: { cascadeNumber?: number; cascadeMode?: string; lightInstance: DirectionalLight }) { 13 | const shadowOptions = Object.assign({}, defaultCascadedShadowOptions, options); 14 | 15 | const cascadedShadowOptions = DirectionalLightCascadedShadow._getCascadedShadowOptions( 16 | shadowOptions.cascadeNumber, 17 | shadowOptions.shadowMapSize, 18 | shadowOptions.lightInstance, 19 | shadowOptions.cascadeMode 20 | ); 21 | super(cascadedShadowOptions); 22 | this.type = "directionalLightCascadedShadow"; 23 | super.init(); 24 | } 25 | 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | public update(light: Light) { 28 | this.updateCascadeFrustumArray(); 29 | this.updateCameraMatrixBySubFrustum(); 30 | if (this.currentViewportIndex == this.vpMatrixArray.length - 1) this.vpMatrixArrayDirty = true; 31 | } 32 | 33 | static _getCascadedShadowOptions( 34 | cascadeNumber: number, 35 | shadowMapSize: Vector2, 36 | lightInstance: DirectionalLight, 37 | cascadeMode: string 38 | ) { 39 | const vpMatrixArrayLength = cascadeNumber > 8 ? 8 : cascadeNumber; 40 | const vpMatrixArray = []; 41 | for (let i = 0; i < vpMatrixArrayLength; i++) { 42 | vpMatrixArray.push(new Matrix4()); 43 | } 44 | return { 45 | vpMatrixArray, 46 | shadowMapSize, 47 | lightInstance, 48 | cascadeMode 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/phong/phongFunction.wgsl: -------------------------------------------------------------------------------- 1 | fn G_BlinnPhong_Implicit() -> f32 { 2 | 3 | //geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v) 4 | return 0.25; 5 | 6 | } 7 | fn D_BlinnPhong(shininess : f32, dotNH : f32) -> f32 { 8 | 9 | return reciprocal_pi * (shininess * 0.5 + 1.0) * pow(dotNH, shininess); 10 | 11 | } 12 | fn BRDF_BlinnPhong(lightDir : vec3 , viewDir : vec3 , normal : vec3 , specularColor : vec3 , shininess : f32) -> vec3 { 13 | 14 | let halfDir = normalize(lightDir + viewDir); 15 | 16 | let dotNH : f32 = saturate(dot(normal, halfDir) ); 17 | let dotVH : f32 = saturate(dot(viewDir, halfDir) ); 18 | 19 | let F = F_Schlick(specularColor, 1.0, dotVH); 20 | 21 | let G : f32 = G_BlinnPhong_Implicit(); 22 | 23 | let D = D_BlinnPhong(shininess, dotNH); 24 | 25 | return F * (G * D); 26 | 27 | } 28 | fn RE_Direct_BlinnPhong(directLight : IncidentLight, geometry : GeometricContext, material : BlinnPhongMaterial) -> ReflectedLight{ 29 | var reflectedLight : ReflectedLight; 30 | let dotNL : f32 = saturate(dot(geometry.normal, directLight.direction)); 31 | let irradiance : vec3 = dotNL * directLight.color; 32 | 33 | reflectedLight.directDiffuse = irradiance * BRDF_Lambert(material.diffuseColor); 34 | 35 | reflectedLight.directSpecular = irradiance * BRDF_BlinnPhong(directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess) * material.specularStrength; 36 | return reflectedLight; 37 | } 38 | fn RE_IndirectDiffuse_BlinnPhong(irradiance : vec3 , geometry : GeometricContext, material : BlinnPhongMaterial) -> ReflectedLight { 39 | var reflectedLight : ReflectedLight; 40 | reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert(material.diffuseColor); 41 | return reflectedLight; 42 | } 43 | -------------------------------------------------------------------------------- /src/shader/material/phongFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct MaterialUniform { 2 | modelMatrix : mat4x4 , 3 | color : vec3 , 4 | opacity : f32, 5 | normalMatrix : mat4x4 , 6 | emissive : vec3 , 7 | shininess : f32, 8 | specular : vec3 , 9 | } 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | @binding(phongBinding) @group(0) var materialUniform : MaterialUniform; 16 | @binding(cameraBinding) @group(1) var systemUniform : SystemUniform; 17 | @fragment 18 | fn main(input : FragInput) -> @location(0) vec4 { 19 | var totalEmissiveRadiance : vec3 = materialUniform.emissive; 20 | var color : vec4 ; 21 | #if USE_BASECOLORTEXTURE 22 | color = vec4 (textureSample(baseColorTexture, baseColorSampler, input.uv).rgb + materialUniform.color, materialUniform.opacity); 23 | #else 24 | color = vec4 (materialUniform.color, materialUniform.opacity); 25 | #endif 26 | let v : vec3 = normalize(systemUniform.cameraPosition - input.worldPos); 27 | #if USE_NORMALTEXTURE 28 | let n : vec3 = getNormalByNormalTexture(input); 29 | #else 30 | let n : vec3 = getNormal(input); 31 | #endif 32 | var geometry : Geometry; 33 | geometry.normal = n; 34 | geometry.viewDir = v; 35 | geometry.position = input.worldPos; 36 | geometry.viewPosition = input.viewPosition.xyz; 37 | let lightColor : ReflectedLight = parseLights(geometry, materialUniform.shininess); 38 | //var finnalColor:vec3=color.xyz + (lightColor.directDiffuse + lightColor.directSpecular + lightColor.ambient); 39 | var finnalColor : vec3 = color.xyz * (lightColor.directDiffuse + lightColor.directSpecular + lightColor.ambient); 40 | 41 | // finnalColor = lightColor.testColor.xyz; 42 | 43 | return vec4 (finnalColor, color.a); 44 | } 45 | -------------------------------------------------------------------------------- /src/render/RenderPass.ts: -------------------------------------------------------------------------------- 1 | import { ShaderDataFactory } from "../core/ShaderDataFactory"; 2 | import { Pass, ShaderDataEnum } from "../core/WebGPUTypes"; 3 | import { Mesh } from "../mesh/Mesh"; 4 | import { ShaderSource } from "../shader/ShaderSource"; 5 | import { RenderState } from "./RenderState"; 6 | import ShaderData from "./ShaderData"; 7 | 8 | export class RenderPass { 9 | public passType: Pass; 10 | private _shaderSource: ShaderSource; 11 | private _shaderData: ShaderData; 12 | private _label: string; 13 | private _shaderDataEnum: ShaderDataEnum; 14 | private _renderState: RenderState; 15 | get shaderSource() { 16 | return this._shaderSource; 17 | } 18 | get shaderData() { 19 | return this._shaderData; 20 | } 21 | set defines(defines) { 22 | this._shaderSource.setDefines(Object.assign({}, defines)); 23 | } 24 | constructor(params: { shaderDataEnum: ShaderDataEnum; passType?: Pass; label?: string }) { 25 | const { label, passType = Pass.RENDER, shaderDataEnum } = params; 26 | this._label = label; 27 | this.passType = passType; 28 | this._shaderDataEnum = shaderDataEnum; 29 | } 30 | get renderState() { 31 | return this._renderState; 32 | } 33 | set renderState(value) { 34 | this._renderState = value; 35 | } 36 | update(mesh?: Mesh) { 37 | const { material, geometry } = mesh; 38 | const defines = Object.assign({}, geometry?.defines, material?.defines, this?.shaderData?.defines) ?? {}; 39 | if (!this._shaderData || material?.dirty) 40 | this._shaderData = ShaderDataFactory.createShaderData({ 41 | label: this._label, 42 | mesh, 43 | material, 44 | shaderDataEnum: this._shaderDataEnum 45 | }); 46 | if (!this._shaderSource) 47 | this._shaderSource = new ShaderSource({ 48 | shaderId: this._label ?? material?.type 49 | }); 50 | this._shaderSource.setDefines(defines); 51 | } 52 | destroy() { 53 | this?._shaderData?.destroy(); 54 | this?._shaderSource?.destroy(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/camera/OrthographicCamera.ts: -------------------------------------------------------------------------------- 1 | import Matrix4 from "../math/Matrix4"; 2 | import Camera from "./Camera"; 3 | export default class OrthographicCamera extends Camera { 4 | _right: number; 5 | isOrthographicCamera: boolean; 6 | _bottom: number; 7 | _left: number; 8 | _near: number; 9 | _far: number; 10 | _top: number; 11 | constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { 12 | super(); 13 | this.near = near; 14 | this.far = far; 15 | this.left = left; 16 | this.top = top; 17 | this.bottom = bottom; 18 | this.right = right; 19 | this.isOrthographicCamera = true; 20 | } 21 | 22 | set left(value: number) { 23 | this._left = value; 24 | this.projectMatrixDirty = true; 25 | } 26 | 27 | get left() { 28 | return this._left; 29 | } 30 | 31 | set right(value: number) { 32 | this._right = value; 33 | this.projectMatrixDirty = true; 34 | } 35 | 36 | get right() { 37 | return this._right; 38 | } 39 | 40 | set top(value: number) { 41 | this._top = value; 42 | this.projectMatrixDirty = true; 43 | } 44 | 45 | get top() { 46 | return this._top; 47 | } 48 | 49 | set bottom(value: number) { 50 | this._bottom = value; 51 | this.projectMatrixDirty = true; 52 | } 53 | 54 | get bottom() { 55 | return this._bottom; 56 | } 57 | 58 | private updateCameraParms() { 59 | const dx = (this.right - this.left) / 2; 60 | const dy = (this.top - this.bottom) / 2; 61 | const cx = (this.right + this.left) / 2; 62 | const cy = (this.top + this.bottom) / 2; 63 | return { 64 | left: cx - dx, 65 | right: cx + dx, 66 | top: cy + dy, 67 | bottom: cy - dy 68 | }; 69 | } 70 | public updateProjectionMatrix() { 71 | if (this.projectMatrixDirty) { 72 | const { left, right, top, bottom } = this.updateCameraParms(); 73 | this._projectionMatrix = Matrix4.makeOrthographic(left, right, top, bottom, this.near, this.far); 74 | this.projectMatrixDirty = false; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { TextureViewDimension } from "../core/WebGPUConstant"; 2 | import Texture from "../render/Texture"; 3 | export async function loadPbrTexture(brdf, diffuse, specular) { 4 | if (!brdf) return; 5 | const brdfTexture = await loadTexture(brdf); 6 | const diffuseTexture = await loadCubeTexture(diffuse); 7 | const specularTexture = await loadCubeTexture(specular); 8 | return { 9 | brdfTexture, 10 | diffuseTexture, 11 | specularTexture 12 | }; 13 | } 14 | export async function loadCubeTexture(urls) { 15 | const promises = urls.map((src) => { 16 | const img = document.createElement("img"); 17 | img.src = src; 18 | return img.decode().then(() => createImageBitmap(img)); 19 | }); 20 | const images = await Promise.all(promises); 21 | await Promise.all(images); 22 | const data = images.map((image, i) => { 23 | return { 24 | source: image, 25 | width: image.width, 26 | height: image.height, 27 | depth: 1, 28 | x: 0, 29 | y: 0, 30 | z: i 31 | }; 32 | }); 33 | return new Texture({ 34 | generateMipmap: true, 35 | textureDescriptor: { 36 | // mipLevelCount: 6, 37 | size: { 38 | width: images[0].width, 39 | height: images[0].height, 40 | depth: 6 41 | }, 42 | format: "rgba8unorm", 43 | usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT 44 | }, 45 | data, 46 | textureViewDescriptor: { 47 | dimension: TextureViewDimension.Cube 48 | } 49 | }); 50 | } 51 | export async function loadTexture(url) { 52 | const img = document.createElement("img"); 53 | img.src = url; 54 | await img.decode(); 55 | const imageBitmap = await createImageBitmap(img); 56 | const baseTexture = new Texture({ 57 | data: { 58 | source: imageBitmap 59 | }, 60 | textureDescriptor: { 61 | size: { width: imageBitmap.width, height: imageBitmap.height, depth: 1 }, 62 | format: "rgba8unorm" 63 | } 64 | }); 65 | return baseTexture; 66 | } 67 | -------------------------------------------------------------------------------- /src/light/Light.ts: -------------------------------------------------------------------------------- 1 | import RenderObject from "../core/RenderObject"; 2 | import { LightType, RenderObjectType } from "../core/WebGPUTypes"; 3 | import Vector3 from "../math/Vector3"; 4 | import { BaseShadow } from "./shadows/BaseShadow"; 5 | 6 | export class Light extends RenderObject { 7 | private _color: Vector3; 8 | type: RenderObjectType; 9 | private _intensity: number; 10 | dirty: boolean; 11 | colorDirty: boolean; 12 | shadowDirty: boolean; 13 | intensityDirty: boolean; 14 | positionDirty: boolean; 15 | public _shadow: BaseShadow; 16 | public targetDirty: boolean; 17 | public lightType: LightType; 18 | 19 | constructor(color: Vector3, intensity: number) { 20 | super(); 21 | this.type = RenderObjectType.Light; 22 | this._color = Vector3.multiplyByScalar(color, intensity, new Vector3()); 23 | this._intensity = intensity; 24 | this._position = new Vector3(0, 1, 0); 25 | this._target = new Vector3(); 26 | this.positionDirty = true; 27 | this.targetDirty = true; 28 | this.colorDirty = true; 29 | this.shadowDirty = true; 30 | this.intensityDirty = true; 31 | this._shadow = null; 32 | } 33 | 34 | get position() { 35 | return this._position; 36 | } 37 | 38 | set position(value) { 39 | this.positionDirty = true; 40 | this._position = value; 41 | } 42 | 43 | get target() { 44 | return this._target; 45 | } 46 | 47 | set target(value) { 48 | this.targetDirty = true; 49 | this._target = value; 50 | } 51 | 52 | get color() { 53 | return this._color; 54 | } 55 | 56 | set color(value) { 57 | this.colorDirty = true; 58 | this._color = value; 59 | } 60 | 61 | set intensity(value) { 62 | this.color = Vector3.multiplyByScalar(this.color, value, new Vector3()); 63 | this.intensityDirty = true; 64 | this._intensity = value; 65 | } 66 | 67 | get intensity() { 68 | return this._intensity; 69 | } 70 | 71 | get shadow() { 72 | return this._shadow; 73 | } 74 | 75 | set shadow(value) { 76 | this.shadowDirty = true; 77 | this._shadow = value; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/light/lightCommon.wgsl: -------------------------------------------------------------------------------- 1 | struct ReflectedLight { 2 | directDiffuse : vec3 , 3 | directSpecular : vec3 , 4 | indirectDiffuse : vec3 , 5 | indirectSpecular : vec3 , 6 | }; 7 | struct Geometry { 8 | position : vec3 , 9 | normal : vec3 , 10 | viewDir : vec3 , 11 | #if USE_CLEARCOAT 12 | vec3 clearcoatNormal; 13 | #endif 14 | }; 15 | fn getAmbientLightIrradiance(ambientLightColor : vec3 ) -> vec3 { 16 | let irradiance = ambientLightColor; 17 | return irradiance; 18 | } 19 | fn getDistanceAttenuation(lightDistance : f32, cutoffDistance : f32, decayExponent : f32) -> f32 { 20 | if (cutoffDistance > 0.0 && decayExponent > 0.0) 21 | { 22 | let x : f32 = saturate(- lightDistance / cutoffDistance + 1.0); 23 | return pow(x, decayExponent); 24 | } 25 | return 1.0; 26 | } 27 | fn getSpotAttenuation(coneCosine : f32, penumbraCosine : f32, angleCosine : f32) -> f32 { 28 | return smoothstep(coneCosine, penumbraCosine, angleCosine); 29 | } 30 | fn shGetIrradianceAt(normal : vec3 , shCoefficients : array, 9>) -> vec3 { 31 | let x : f32 = normal.x; 32 | let y : f32 = normal.y; 33 | let z : f32 = normal.z; 34 | var result : vec3 = shCoefficients[ 0 ] * 0.886227; 35 | result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; 36 | result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; 37 | result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; 38 | result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; 39 | result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; 40 | result += shCoefficients[ 6 ] * (0.743125 * z * z - 0.247708); 41 | result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; 42 | result += shCoefficients[ 8 ] * 0.429043 * (x * x - y * y); 43 | return result; 44 | } 45 | fn inverseTransformDirection(dir : vec3 , matrix : mat4x4 ) -> vec3 { 46 | return normalize((vec4 (dir, 0.0) * matrix).xyz); 47 | } 48 | -------------------------------------------------------------------------------- /src/geometry/PlaneGeometry.ts: -------------------------------------------------------------------------------- 1 | import { Float32Attribute } from "../render/Attribute"; 2 | import Geometry from "./Geometry"; 3 | export default class PlaneGeometry extends Geometry { 4 | constructor(public width: number = 10, public height: number = 10) { 5 | super({ 6 | type: "planeGeometry" 7 | }); 8 | this.defines = { 9 | HAS_NORMAL: true 10 | }; 11 | this.init(); 12 | } 13 | private init() { 14 | // generate pos uv normal so on 15 | const { indices, normals, uvs, vertices } = this.createGrid(this.width, this.height); 16 | this.computeBoundingSphere(vertices); 17 | this.setAttribute(new Float32Attribute("position", vertices, 3)); 18 | this.setAttribute(new Float32Attribute("normal", normals, 3)); 19 | this.setAttribute(new Float32Attribute("uv", uvs, 2)); 20 | this.setIndice(indices); 21 | this.count = indices.length; 22 | } 23 | private createGrid(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { 24 | const width_half = width / 2; 25 | const height_half = height / 2; 26 | 27 | const gridX = Math.floor(widthSegments); 28 | const gridY = Math.floor(heightSegments); 29 | 30 | const gridX1 = gridX + 1; 31 | const gridY1 = gridY + 1; 32 | 33 | const segment_width = width / gridX; 34 | const segment_height = height / gridY; 35 | 36 | // 37 | 38 | const indices = []; 39 | const vertices = []; 40 | const normals = []; 41 | const uvs = []; 42 | 43 | for (let iy = 0; iy < gridY1; iy++) { 44 | const y = iy * segment_height - height_half; 45 | 46 | for (let ix = 0; ix < gridX1; ix++) { 47 | const x = ix * segment_width - width_half; 48 | 49 | vertices.push(x, -y, 0); 50 | 51 | normals.push(0, 0, 1); 52 | 53 | uvs.push(ix / gridX); 54 | uvs.push(1 - iy / gridY); 55 | } 56 | } 57 | 58 | for (let iy = 0; iy < gridY; iy++) { 59 | for (let ix = 0; ix < gridX; ix++) { 60 | const a = ix + gridX1 * iy; 61 | const b = ix + gridX1 * (iy + 1); 62 | const c = ix + 1 + gridX1 * (iy + 1); 63 | const d = ix + 1 + gridX1 * iy; 64 | 65 | indices.push(a, b, d); 66 | indices.push(b, c, d); 67 | } 68 | } 69 | return { indices, normals, uvs, vertices }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/render/BindGroup.ts: -------------------------------------------------------------------------------- 1 | import { BindGroupCacheOptions } from "../core/WebGPUTypes"; 2 | const bindGroupCache = new Map(); 3 | class BindGroup { 4 | device: GPUDevice; 5 | gpuBindGroup: GPUBindGroup; 6 | label: string; 7 | index: number; 8 | dirty: boolean; 9 | offset?: number; 10 | // const uniformBytes = 5 * Float32Array.BYTES_PER_ELEMENT; 11 | // const alignedSizeBytes = Math.ceil(uniformBytes / 256) * 256; 12 | // const alignedSize =alignedSizeBytes / Float32Array.BYTES_PER_ELEMENT; 13 | alignedSize?: number; 14 | maxOffset?: number; 15 | dynamic?: boolean; 16 | constructor(options: BindGroupCacheOptions) { 17 | this.index = options.index || 0; 18 | this.offset = options.offset ?? 0; 19 | this.alignedSize = options.alignedSize ?? 0; 20 | this.maxOffset = options.maxOffset ?? 0; 21 | this.dynamic = options.dynamic ?? false; 22 | this.gpuBindGroup = options.device.createBindGroup({ 23 | label: options.label, 24 | layout: options.layout.gpuBindGroupLayout, 25 | entries: options.entires.map((entity) => ({ 26 | binding: entity.binding, 27 | resource: entity.resource 28 | })) 29 | }); 30 | } 31 | bind(passEncoder: GPURenderPassEncoder | GPUComputePassEncoder) { 32 | // dynamic uniforms must bind multiple times 33 | if (this.dynamic) { 34 | const dynamicOffsets = [0]; 35 | dynamicOffsets[0] = this.offset * this.alignedSize; 36 | this.offset = ++this.offset < this.maxOffset ? this.offset : 0; 37 | passEncoder.setBindGroup(this.index, this.gpuBindGroup, dynamicOffsets); 38 | } else { 39 | passEncoder.setBindGroup(this.index, this.gpuBindGroup); 40 | } 41 | } 42 | destroy() { 43 | this.gpuBindGroup = undefined; 44 | this.device = undefined; 45 | } 46 | static getBindGroupFromCache(options: BindGroupCacheOptions): BindGroup { 47 | if (bindGroupCache.has(options.label)) { 48 | return bindGroupCache.get(options.label); 49 | } else { 50 | const bindGroup = new BindGroup(options); 51 | bindGroupCache.set(options.label, bindGroup); 52 | return bindGroup; 53 | } 54 | } 55 | static removeBindGroupFromCache(bindGroup: BindGroup) { 56 | bindGroupCache.delete(bindGroup); 57 | } 58 | } 59 | 60 | export default BindGroup; 61 | -------------------------------------------------------------------------------- /src/core/TextureCache.ts: -------------------------------------------------------------------------------- 1 | import Sampler from "../render/Sampler"; 2 | import Texture from "../render/Texture"; 3 | import defined from "../utils/defined"; 4 | import { destroyObject } from "../utils/destroyObject"; 5 | type TextureCacheProp = { 6 | texture: Texture; 7 | count: number; 8 | }; 9 | class TextureCache { 10 | public defaultSampler: Sampler; 11 | private _numberOfTextures: number; 12 | private _textures: Map; 13 | private _texturesToRelease: Map; 14 | constructor() { 15 | this._numberOfTextures = 0; 16 | this._textures = new Map(); 17 | this._numberOfTextures = 0; 18 | this._texturesToRelease = new Map(); 19 | this.defaultSampler = new Sampler({ 20 | magFilter: "linear", 21 | minFilter: "linear", 22 | addressModeU: "repeat", 23 | addressModeV: "repeat" 24 | }); 25 | } 26 | get numberOfTextures(): number { 27 | return this._numberOfTextures; 28 | } 29 | getTexture(keyword) { 30 | const cachedTexture = this._textures.get(keyword); 31 | if (!defined(cachedTexture)) { 32 | return undefined; 33 | } 34 | // No longer want to release this if it was previously released. 35 | delete this._texturesToRelease[keyword]; 36 | 37 | ++cachedTexture.count; 38 | return cachedTexture.texture; 39 | } 40 | addTexture(keyword, texture) { 41 | const cachedTexture = { 42 | texture: texture, 43 | count: 1 44 | }; 45 | 46 | texture.finalDestroy = texture.destroy; 47 | 48 | // const that = this; 49 | texture.destroy = () => { 50 | if (--cachedTexture.count === 0) { 51 | this._texturesToRelease.set(keyword, cachedTexture); 52 | } 53 | }; 54 | 55 | this._textures.set(keyword, cachedTexture); 56 | ++this._numberOfTextures; 57 | } 58 | releasedTextures() { 59 | this._texturesToRelease.forEach((cacheTexture) => { 60 | cacheTexture.texture?.finalDestroy(); 61 | --this._numberOfTextures; 62 | }); 63 | 64 | this._texturesToRelease.clear(); 65 | } 66 | destroy() { 67 | this._textures?.forEach?.((cachedTexture) => { 68 | cachedTexture.texture?.finalDestroy(); 69 | }); 70 | return destroyObject(this); 71 | } 72 | } 73 | const textureCache = new TextureCache(); 74 | export default textureCache; 75 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/phong/blinn_phong.wgsl: -------------------------------------------------------------------------------- 1 | fn getPointLightInfo(pointLight : PointLight, worldPos : vec3 , shininess : f32, N : vec3 , V : vec3 ) -> vec3 { 2 | var color = vec3 (0.0, 0.0, 0.0); 3 | var direction : vec3 = worldPos - pointLight.position; 4 | let dist : f32 = length(direction); 5 | direction = normalize(direction); 6 | let decay = clamp(1.0 - pow(dist / pointLight.distance, 4.0), 0.0, 1.0); 7 | 8 | let d = max(dot(N, -direction), 0.0) * decay; 9 | color += pointLight.color * d; 10 | 11 | let halfDir : vec3 = normalize(V - direction); 12 | let s : f32 = pow(clamp(dot(N, halfDir), 0.0, 1.0), shininess) * decay; 13 | color += pointLight.color * s; 14 | return color; 15 | } 16 | fn getSpotLightInfo(spotLight : SpotLight, worldPos : vec3 , shininess : f32, N : vec3 , V : vec3 ) -> vec3 { 17 | var color = vec3 (0.0, 0.0, 0.0); 18 | var direction : vec3 = spotLight.position - worldPos; 19 | let lightDistance : f32 = length(direction); 20 | direction = normalize(direction); 21 | let angleCos : f32 = dot(direction, -spotLight.direction); 22 | let decay : f32 = clamp(1.0 - pow(lightDistance / spotLight.distance, 4.0), 0.0, 1.0); 23 | let spotEffect : f32 = smoothstep(spotLight.penumbraCos, spotLight.coneCos, angleCos); 24 | let decayTotal : f32 = decay * spotEffect; 25 | let d : f32 = max(dot(N, direction), 0.0) * decayTotal; 26 | color += spotLight.color * d; 27 | let halfDir : vec3 = normalize(V + direction); 28 | let s : f32 = pow(clamp(dot(N, halfDir), 0.0, 1.0), shininess) * decayTotal; 29 | color += spotLight.color * s; 30 | return color; 31 | } 32 | struct DirectionalLight { 33 | direction : vec3 , 34 | color : vec3 , 35 | }; 36 | fn getDirectLightInfo(directionalLight : DirectionalLight, shininess : f32, N : vec3 , V : vec3 ) -> vec3 { 37 | var color = vec3 (0.0, 0.0, 0.0); 38 | let d : f32 = max(dot(N, -directionalLight.direction), 0.0); 39 | color += directionalLight.color * d; 40 | 41 | let halfDir : vec3 = normalize(V - directionalLight.direction); 42 | let s : f32 = pow(clamp(dot(N, halfDir), 0.0, 1.0), shininess); 43 | color += directionalLight.color * s; 44 | return color; 45 | } 46 | -------------------------------------------------------------------------------- /src/light/shadows/BaseShadow.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../../camera/Camera"; 2 | import { TextureFormat, TextureSampleType, TextureUsage } from "../../core/WebGPUConstant"; 3 | import Vector2 from "../../math/Vector2"; 4 | import Vector4 from "../../math/Vector4"; 5 | import Texture from "../../render/Texture"; 6 | export class BaseShadow { 7 | protected _shadowMapSize: Vector2; 8 | protected _camera: Camera; 9 | protected _cameraArray: Array; 10 | protected _shadowMap: Texture; 11 | public type: string; 12 | public _viewports: Array; 13 | public viewportSize: Vector2; 14 | public currentViewportIndex: number; 15 | public viewPortDirty: boolean; 16 | public vpMatrixDirty: boolean; 17 | isCascadedShadow: boolean; 18 | 19 | constructor(shadowMapSize: Vector2, camera: Camera | Array) { 20 | this._shadowMapSize = shadowMapSize; 21 | this._camera = Array.isArray(camera) ? undefined : camera; 22 | this._cameraArray = Array.isArray(camera) ? camera : undefined; 23 | this.viewPortDirty = true; 24 | this.vpMatrixDirty = true; 25 | } 26 | 27 | get camera() { 28 | return this._camera; 29 | } 30 | 31 | get cameraArray() { 32 | return this._cameraArray; 33 | } 34 | 35 | get shadowMapSize() { 36 | return this._shadowMapSize; 37 | } 38 | 39 | get viewports() { 40 | return this._viewports; 41 | } 42 | 43 | public getShadowMapTexture() { 44 | return this._shadowMap; 45 | } 46 | 47 | init() { 48 | this._initShadowMapTexture(); 49 | } 50 | 51 | protected _initShadowMapTexture() { 52 | this._createShadowMapTexture(); 53 | } 54 | 55 | protected _createShadowMapTexture() { 56 | this._shadowMap = new Texture({ 57 | label: `${this.type}Map`, 58 | fixedSize: true, 59 | sampleType: TextureSampleType.Depth, 60 | textureDescriptor: { 61 | size: { 62 | width: this._shadowMapSize.x, 63 | height: this._shadowMapSize.y, 64 | depth: 1 65 | }, 66 | format: TextureFormat.Depth24Plus, 67 | usage: 68 | TextureUsage.RenderAttachment | 69 | TextureUsage.TextureBinding | 70 | TextureUsage.CopySrc | 71 | TextureUsage.CopyDst 72 | } 73 | }); 74 | } 75 | 76 | // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars 77 | public update(light) {} 78 | } 79 | -------------------------------------------------------------------------------- /src/render/QuerySet.ts: -------------------------------------------------------------------------------- 1 | import Buffer from "./Buffer"; 2 | export default class QuerySet { 3 | public gpuQuerySet: GPUQuerySet; 4 | public queryBuffer: Buffer; 5 | public nextQueryIndex: Uint32Array; 6 | public readBuffer: Buffer; 7 | private device: GPUDevice; 8 | constructor(public querySetDescriptor: GPUQuerySetDescriptor) { 9 | this.nextQueryIndex = new Uint32Array(1); 10 | } 11 | public update(device: GPUDevice) { 12 | if (!this.device) this.device = device; 13 | this.gpuQuerySet = device.createQuerySet(this.querySetDescriptor); 14 | this.queryBuffer = Buffer.create( 15 | "querySave", 16 | device, 17 | GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC, 18 | null, 19 | this.querySetDescriptor.count * 8 20 | ); 21 | this.readBuffer = Buffer.create( 22 | "queryRead", 23 | device, 24 | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC, 25 | null, 26 | this.querySetDescriptor.count * 8 27 | ); 28 | } 29 | private async getReadBuffer() { 30 | const commandEncoder = this.device.createCommandEncoder(); 31 | // 因为查询结果都是Uint64,需要八个字节,NUM_SPHERES数量必须与查询的数量一致 32 | commandEncoder.resolveQuerySet(this.gpuQuerySet, 0, this.nextQueryIndex[0], this.queryBuffer.gpuBuffer, 0); 33 | commandEncoder.copyBufferToBuffer( 34 | this.queryBuffer.gpuBuffer, 35 | 0, 36 | this.readBuffer.gpuBuffer, 37 | 0, 38 | 8 * this.nextQueryIndex[0] 39 | ); 40 | this.device.queue.submit([commandEncoder.finish()]); 41 | } 42 | public async getQuerySetResult() { 43 | this.getReadBuffer(); 44 | await this.readBuffer.gpuBuffer.mapAsync(GPUMapMode.READ); 45 | const copyArrayBuffer = this.readBuffer.gpuBuffer.getMappedRange(); 46 | const array = new BigUint64Array(copyArrayBuffer).slice(); 47 | this.readBuffer.gpuBuffer.unmap(); 48 | return array; 49 | } 50 | beginQuery(renderPass: GPURenderPassEncoder, queryIndex: number) { 51 | renderPass.beginOcclusionQuery(queryIndex); 52 | } 53 | endQuery(renderPass: GPURenderPassEncoder) { 54 | renderPass.endOcclusionQuery(); 55 | } 56 | getQueryIndex() { 57 | ++this.nextQueryIndex[0]; 58 | const key = this.nextQueryIndex[0]; 59 | if (key === 0) { 60 | // In case of overflow 61 | throw new Error("Out of QueryIndex."); 62 | } 63 | return key; 64 | } 65 | destroy() { 66 | this.gpuQuerySet.destroy(); 67 | this.queryBuffer.destroy(); 68 | this.readBuffer.destroy(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/shader/postProcess/fxaa/fxaa.wgsl: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FXAA_REDUCE_MIN 3 | #define FXAA_REDUCE_MIN (1.0 / 128.0) 4 | #endif 5 | #ifndef FXAA_REDUCE_MUL 6 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 7 | #endif 8 | #ifndef FXAA_SPAN_MAX 9 | #define FXAA_SPAN_MAX 8.0 10 | #endif 11 | 12 | //optimized version for mobile, where dependent 13 | //texture reads can be a bottleneck 14 | vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution, 15 | vec2 v_rgbNW, vec2 v_rgbNE, 16 | vec2 v_rgbSW, vec2 v_rgbSE, 17 | vec2 v_rgbM) 18 | { 19 | vec4 color; 20 | mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); 21 | vec3 rgbNW = texture2D(tex, v_rgbNW).xyz; 22 | vec3 rgbNE = texture2D(tex, v_rgbNE).xyz; 23 | vec3 rgbSW = texture2D(tex, v_rgbSW).xyz; 24 | vec3 rgbSE = texture2D(tex, v_rgbSE).xyz; 25 | vec4 texColor = texture2D(tex, v_rgbM); 26 | vec3 rgbM = texColor.xyz; 27 | vec3 luma = vec3(0.299, 0.587, 0.114); 28 | float lumaNW = dot(rgbNW, luma); 29 | float lumaNE = dot(rgbNE, luma); 30 | float lumaSW = dot(rgbSW, luma); 31 | float lumaSE = dot(rgbSE, luma); 32 | float lumaM = dot(rgbM, luma); 33 | float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); 34 | float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); 35 | 36 | mediump vec2 dir; 37 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 38 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 39 | 40 | float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 41 | (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); 42 | 43 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 44 | dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), 45 | max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 46 | dir * rcpDirMin)) * inverseVP; 47 | 48 | vec3 rgbA = 0.5 * ( 49 | texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + 50 | texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); 51 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 52 | texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + 53 | texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); 54 | 55 | float lumaB = dot(rgbB, luma); 56 | if ((lumaB < lumaMin) || (lumaB > lumaMax)) 57 | color = vec4(rgbA, texColor.a); 58 | else 59 | color = vec4(rgbB, texColor.a); 60 | return color; 61 | } 62 | -------------------------------------------------------------------------------- /src/camera/PointLightShadowCamera.ts: -------------------------------------------------------------------------------- 1 | import { BufferUsage } from "../core/WebGPUConstant"; 2 | import { UniformEnum } from "../core/WebGPUTypes"; 3 | import Matrix4 from "../math/Matrix4"; 4 | import ShaderData from "../render/ShaderData"; 5 | import UniformBuffer from "../render/UniformBuffer"; 6 | import PerspectiveCamera from "./PerspectiveCamera"; 7 | 8 | export default class PointLightShadowCamera extends PerspectiveCamera { 9 | public vpMatrixArray: Array; 10 | public vpMatrixIndexArray: Array; 11 | 12 | constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { 13 | super(fov, aspect, near, far); 14 | this.vpMatrixArray = [new Matrix4(), new Matrix4(), new Matrix4(), new Matrix4(), new Matrix4(), new Matrix4()]; 15 | } 16 | 17 | // createShaderData() { 18 | // this.shaderData = new ShaderData("camera", 0, 1, 1); 19 | 20 | // const uniformBuffer = new UniformBuffer({ 21 | // label: "pointLightShadowCamera", 22 | // type: "read-only-storage", 23 | // usage: BufferUsage.Storage | BufferUsage.CopyDst, 24 | // hasDynamicOffset: true, 25 | // minBindingSize: 64, 26 | // maxOffset: 6, 27 | // size: 256 * 6 28 | // }); 29 | // uniformBuffer.setMatrix4Array( 30 | // "vpMatrixArray", 31 | // () => { 32 | // if (this.vpMatrixArray.length != 6) throw new Error("pointLightShadowCamera uniformBuffer has Error"); 33 | 34 | // return this.vpMatrixArray; 35 | // }, 36 | // 6, 37 | // 256 38 | // ); 39 | 40 | // this.shaderData.setUniformBuffer("pointLightShadowCamera", uniformBuffer); 41 | // this.shaderData.setDefine("isPointLightShadowMap", true); 42 | // } 43 | 44 | createShaderData() { 45 | this.shaderData = new ShaderData("camera", 0, 1, 1); 46 | 47 | const uniformBuffer = new UniformBuffer({ 48 | label: "pointLightShadowCamera", 49 | type: "read-only-storage", 50 | usage: BufferUsage.Storage | BufferUsage.CopyDst 51 | }); 52 | uniformBuffer.setUniform( 53 | "vpMatrix", 54 | () => { 55 | return this.vpMatrix; 56 | }, 57 | UniformEnum.Mat4 58 | ); 59 | 60 | this.shaderData.setUniformBuffer("pointLightShadowCamera", uniformBuffer); 61 | this.shaderData.setDefine("isPointLightShadowMap", true); 62 | this.shaderData.setDefine("IS_POINTLIGHT_SHADOWMAP", true); 63 | } 64 | 65 | updateVpMatrixArrayAndIndex(index: number) { 66 | Matrix4.clone(this.vpMatrix, this.vpMatrixArray[index]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/combine.ts: -------------------------------------------------------------------------------- 1 | import defaultValue from "./defaultValue"; 2 | import defined from "./defined"; 3 | 4 | /** 5 | * Merges two objects, copying their properties onto a new combined object. When two objects have the same 6 | * property, the value of the property on the first object is used. If either object is undefined, 7 | * it will be treated as an empty object. 8 | * 9 | * @example 10 | * const object1 = { 11 | * propOne : 1, 12 | * propTwo : { 13 | * value1 : 10 14 | * } 15 | * } 16 | * const object2 = { 17 | * propTwo : 2 18 | * } 19 | * const final = combine(object1, object2); 20 | * 21 | * // final === { 22 | * // propOne : 1, 23 | * // propTwo : { 24 | * // value1 : 10 25 | * // } 26 | * // } 27 | * 28 | * @param {Object} [object1] The first object to merge. 29 | * @param {Object} [object2] The second object to merge. 30 | * @param {Boolean} [deep=false] Perform a recursive merge. 31 | * @returns {Object} The combined object containing all properties from both objects. 32 | * 33 | * @function 34 | */ 35 | function combine(object1, object2, deep) { 36 | deep = defaultValue(deep, false); 37 | 38 | const result = {}; 39 | 40 | const object1Defined = defined(object1); 41 | const object2Defined = defined(object2); 42 | let property; 43 | let object1Value; 44 | let object2Value; 45 | if (object1Defined) { 46 | for (property in object1) { 47 | // eslint-disable-next-line no-prototype-builtins 48 | if (object1.hasOwnProperty(property)) { 49 | object1Value = object1[property]; 50 | // eslint-disable-next-line no-prototype-builtins 51 | if (object2Defined && deep && typeof object1Value === "object" && object2.hasOwnProperty(property)) { 52 | object2Value = object2[property]; 53 | if (typeof object2Value === "object") { 54 | result[property] = combine(object1Value, object2Value, deep); 55 | } else { 56 | result[property] = object1Value; 57 | } 58 | } else { 59 | result[property] = object1Value; 60 | } 61 | } 62 | } 63 | } 64 | if (object2Defined) { 65 | for (property in object2) { 66 | // eslint-disable-next-line no-prototype-builtins 67 | if (object2.hasOwnProperty(property) && !result.hasOwnProperty(property)) { 68 | object2Value = object2[property]; 69 | result[property] = object2Value; 70 | } 71 | } 72 | } 73 | return result; 74 | } 75 | export default combine; 76 | -------------------------------------------------------------------------------- /src/post-process/ResolvePostEffect.ts: -------------------------------------------------------------------------------- 1 | import { TextureFormat, TextureUsage } from "../core/WebGPUConstant"; 2 | import ShaderMaterial from "../material/ShaderMaterial"; 3 | import Attachment from "../render/Attachment"; 4 | import Context from "../render/Context"; 5 | import RenderTarget from "../render/RenderTarget"; 6 | import Sampler from "../render/Sampler"; 7 | import Texture from "../render/Texture"; 8 | import getVertFrag from "../shader/Shaders"; 9 | import PostEffect from "./PostEffect"; 10 | export default class ResolvePostEffect extends PostEffect { 11 | material: ShaderMaterial; 12 | constructor() { 13 | super(1024, 1024, "resolve"); 14 | const shader = getVertFrag("resolve", { positionLocation: 0 }); 15 | this.material = new ShaderMaterial({ 16 | shaderId: "resolve", 17 | frag: shader.frag, 18 | vert: shader.vert, 19 | uniformTextureAndSampler: { 20 | texture: { 21 | type: "texture", 22 | value: undefined 23 | }, 24 | sampler: { 25 | type: "sampler", 26 | value: new Sampler({ 27 | magFilter: "linear", 28 | minFilter: "linear" 29 | }) 30 | } 31 | } 32 | }); 33 | this.fullScreenQuad.material = this.material; 34 | } 35 | setSize(width: number, height: number) { 36 | this.currentRenderTarget.setSize(width, height); 37 | this.material.dirty = true; 38 | } 39 | render(context: Context, colorTexture: Texture) { 40 | if (!this.currentRenderTarget) this.initRenderTarget(context); 41 | // this.material 42 | this.material.shaderMaterialParms.uniformTextureAndSampler.texture.value = colorTexture; 43 | this.material.update(undefined, this.fullScreenQuad); 44 | this.renderMesh(context); 45 | return null; 46 | } 47 | private initRenderTarget(context: Context) { 48 | const { width, height, depth } = context.presentationSize; 49 | const colorAttachment = new Attachment( 50 | { r: 0.0, g: 0.0, b: 0.0, a: 0 }, 51 | { 52 | textureView: () => { 53 | return context.context.getCurrentTexture().createView(); 54 | } 55 | } 56 | ); 57 | const depthTexture = new Texture({ 58 | label: "resolveDepth", 59 | textureDescriptor: { 60 | size: { width, height, depth }, 61 | format: TextureFormat.Depth24Plus, 62 | usage: TextureUsage.RenderAttachment 63 | } 64 | }); 65 | const depthAttachment = new Attachment(1.0, { texture: depthTexture }); 66 | this.currentRenderTarget = new RenderTarget("render", [colorAttachment], depthAttachment); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/helper/ShadowMapDebugger.ts: -------------------------------------------------------------------------------- 1 | import { RenderObjectType } from "../core/WebGPUTypes"; 2 | import Geometry from "../geometry/Geometry"; 3 | import PlaneGeometry from "../geometry/PlaneGeometry"; 4 | import { Light } from "../light/Light"; 5 | import ShaderMaterial from "../material/ShaderMaterial"; 6 | import { Mesh } from "../mesh/Mesh"; 7 | import { ViewPort } from "../render/RenderState"; 8 | import Sampler from "../render/Sampler"; 9 | import { Scene } from "../Scene"; 10 | import getVertFrag from "../shader/Shaders"; 11 | 12 | export class ShadowMapDebugger { 13 | private mesh: Mesh; 14 | private debuggerSize: { width: number; height: number }; 15 | public light: Light; 16 | private geometry: Geometry; 17 | private material: ShaderMaterial; 18 | private scene: Scene; 19 | 20 | constructor(light: Light, scene: Scene) { 21 | if (!light || !(light instanceof Light)) throw new Error("The parameter must be Light instance"); 22 | 23 | this.light = light; 24 | this.scene = scene; 25 | this.debuggerSize = { 26 | width: 256, 27 | height: 256 28 | }; 29 | 30 | this.mesh = this._createShadowMapMesh(); 31 | const shadowMap = this.light.shadow.getShadowMapTexture(); 32 | this.material.shaderMaterialParms.uniformTextureAndSampler.texture.value = shadowMap; 33 | this.mesh.type = RenderObjectType.Debug; 34 | this.scene.add(this.mesh); 35 | } 36 | 37 | _createShadowMapMesh() { 38 | const shader = getVertFrag("shadowMapDebugger", { 39 | positionLocation: 0 40 | }); 41 | this.geometry = new PlaneGeometry(2, 2); 42 | this.material = new ShaderMaterial({ 43 | shaderId: "shadowMapDebugger", 44 | frag: shader.frag, 45 | vert: shader.vert, 46 | uniformTextureAndSampler: { 47 | texture: { 48 | type: "texture", 49 | value: undefined 50 | }, 51 | sampler: { 52 | type: "sampler", 53 | value: new Sampler({ 54 | magFilter: "linear", 55 | minFilter: "linear" 56 | }) 57 | } 58 | } 59 | }); 60 | this.material.renderState.viewport = new ViewPort(0, 0, this.debuggerSize.width, this.debuggerSize.height); 61 | return new Mesh(this.geometry, this.material); 62 | } 63 | 64 | setSize(width: number, height: number) { 65 | if (!width || !height) return; 66 | this.debuggerSize.width = width; 67 | this.debuggerSize.height = height; 68 | this.update(); 69 | } 70 | 71 | update() { 72 | this.material.renderState.viewport = new ViewPort(0, 0, this.debuggerSize.width, this.debuggerSize.height); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/mesh/Mesh.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { DerivedCommands } from "../core/DerivedCommands"; 3 | import { FrameState } from "../core/FrameState"; 4 | import LightManger from "../core/LightManger"; 5 | import RenderObject from "../core/RenderObject"; 6 | import { Intersect } from "../core/WebGPUConstant"; 7 | import { Pass, RenderObjectType } from "../core/WebGPUTypes"; 8 | import Geometry from "../geometry/Geometry"; 9 | import { Material } from "../material/Material"; 10 | import DrawCommand from "../render/DrawCommand"; 11 | import createGuid from "../utils/createGuid"; 12 | export class Mesh extends RenderObject { 13 | [x: string]: any; 14 | uid: string; 15 | frustumCull: boolean; 16 | subCommands: { [prop: string]: DrawCommand }; 17 | geometry?: Geometry; 18 | material?: Material; 19 | instanceCount?: number; 20 | priority?: number; 21 | derivedCommands?: DerivedCommands; 22 | constructor(geometry?: Geometry, material?: Material) { 23 | super(); 24 | this.geometry = geometry; 25 | this.material = material; 26 | this.type = RenderObjectType.Mesh; 27 | this.frustumCull = true; 28 | this.uid = createGuid(); 29 | this.subCommands = {}; 30 | this.derivedCommands = new DerivedCommands(); 31 | } 32 | get distanceToCamera(): number { 33 | return this?.geometry?.distanceToCamera; 34 | } 35 | get ready() { 36 | return this.material.ready; 37 | } 38 | update(frameState: FrameState, camera?: Camera) { 39 | // update matrix 40 | this.updateMatrix(this?.parent?.modelMatrix); 41 | // create 42 | this.geometry.update({ frameState, matrix: this.modelMatrix, camera }); 43 | this.material.update(frameState, this); 44 | if (this.type == RenderObjectType.Debug) { 45 | frameState.renderQueue.debugQueue.push(this); 46 | return; 47 | } 48 | const visibility = !this.frustumCull ? Intersect.INSIDE : this.geometry?.intersect; 49 | 50 | // 视锥剔除 51 | if (visibility === Intersect.OUTSIDE) return; 52 | if (this.material.transparent) { 53 | frameState.renderQueue.transparent.push(this); 54 | } else { 55 | frameState.renderQueue.opaque.push(this); 56 | } 57 | } 58 | beforeRender() { 59 | // before render 60 | } 61 | afterRender() { 62 | // after render 63 | } 64 | public getPassCommand(pass = Pass.RENDER, lightManger?: LightManger) { 65 | return this?.derivedCommands?.getDerivedCommand({ pass, lightManger, mesh: this }); 66 | } 67 | destroy() { 68 | this.geometry.destroy(); 69 | this.material.destroy(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/mesh/SKinMesh.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import { BufferBindingType, BufferUsage } from "../core/WebGPUConstant"; 4 | import { RenderObjectType, UniformEnum } from "../core/WebGPUTypes"; 5 | import Geometry from "../geometry/Geometry"; 6 | import { Material } from "../material/Material"; 7 | import Matrix4 from "../math/Matrix4"; 8 | import UniformBuffer from "../render/UniformBuffer"; 9 | import { Mesh } from "./Mesh"; 10 | import Node from "./Node"; 11 | 12 | export class SKinMesh extends Mesh { 13 | inverseBindMatrices: Array; 14 | uniformMatrixs: Array; 15 | joints: Array; 16 | private hasAddJoints: boolean; 17 | constructor(geometry?: Geometry, material?: Material) { 18 | super(geometry, material); 19 | this.type = RenderObjectType.SkinMesh; 20 | this.uniformMatrixs = []; 21 | this.hasAddJoints = false; 22 | } 23 | setSkinData(data: SkinDataType) { 24 | this.inverseBindMatrices = data.inverseBindMatrices; 25 | this.joints = data.joints; 26 | } 27 | update(frameState: FrameState, camera?: Camera) { 28 | this.uniformMatrixs = this.joints.map((joint) => (joint as Node).modelMatrix); 29 | super.update(frameState, camera); 30 | if (!this.hasAddJoints) this.addUniformsToMaterial(); 31 | } 32 | private addUniformsToMaterial() { 33 | if (!this.material.shaderData) return; 34 | this.hasAddJoints = true; 35 | if (this.joints) { 36 | const skinJointsBuffer = new UniformBuffer({ 37 | label: "skinJointsBuffer", 38 | type: BufferBindingType.ReadOnlyStorage, 39 | usage: BufferUsage.Storage | BufferUsage.CopyDst, 40 | size: 3000 41 | }); 42 | const invsBuffer = new UniformBuffer({ 43 | label: "invsBuffer", 44 | type: BufferBindingType.ReadOnlyStorage, 45 | usage: BufferUsage.Storage | BufferUsage.CopyDst, 46 | size: 3000 47 | }); 48 | skinJointsBuffer.setUniform( 49 | "joints", 50 | () => { 51 | return this.uniformMatrixs; 52 | }, 53 | UniformEnum.Mat4Array, 54 | this.uniformMatrixs.length 55 | ); 56 | invsBuffer.setUniform( 57 | "jointsInv", 58 | () => { 59 | return this.inverseBindMatrices; 60 | }, 61 | UniformEnum.Mat4Array, 62 | this.inverseBindMatrices.length 63 | ); 64 | this.material.shaderData.setUniformBuffer("skinJointsBuffer", skinJointsBuffer); 65 | this.material.shaderData.setUniformBuffer("invsBuffer", invsBuffer); 66 | } 67 | } 68 | } 69 | export type SkinDataType = { 70 | inverseBindMatrices?: Array; 71 | joints?: Array; 72 | }; 73 | -------------------------------------------------------------------------------- /src/camera/Camera.ts: -------------------------------------------------------------------------------- 1 | import RenderObject from "../core/RenderObject"; 2 | import { RenderObjectType, UniformEnum } from "../core/WebGPUTypes"; 3 | import Matrix4 from "../math/Matrix4"; 4 | import ShaderData from "../render/ShaderData"; 5 | import UniformBuffer from "../render/UniformBuffer"; 6 | 7 | export default class Camera extends RenderObject { 8 | private _viewMatrix: Matrix4; 9 | protected _projectionMatrix: Matrix4; 10 | private _vpMatrix: Matrix4; 11 | projectMatrixDirty: boolean; 12 | shaderData: ShaderData; 13 | _near: number; 14 | _far: number; 15 | constructor() { 16 | super(); 17 | this._viewMatrix = undefined; 18 | this.type = RenderObjectType.Camera; 19 | this._viewMatrix = new Matrix4(); 20 | this._vpMatrix = new Matrix4(); 21 | this.projectMatrixDirty = true; 22 | this.createShaderData(); 23 | } 24 | 25 | set near(value: number) { 26 | this._near = value; 27 | this.projectMatrixDirty = true; 28 | } 29 | 30 | get near() { 31 | return this._near; 32 | } 33 | 34 | set far(value: number) { 35 | this._far = value; 36 | this.projectMatrixDirty = true; 37 | } 38 | 39 | get far() { 40 | return this._far; 41 | } 42 | 43 | get viewMatrix() { 44 | this.updateMatrix(); 45 | Matrix4.inverse(this.modelMatrix, this._viewMatrix); 46 | return this._viewMatrix; 47 | } 48 | get projectionMatrix() { 49 | this.updateProjectionMatrix(); 50 | return this._projectionMatrix; 51 | } 52 | 53 | get vpMatrix() { 54 | Matrix4.multiply(this.projectionMatrix, this.viewMatrix, this._vpMatrix); 55 | return this._vpMatrix; 56 | } 57 | 58 | get inverseViewMatrix() { 59 | this.updateMatrix(); 60 | return this.modelMatrix; 61 | } 62 | // eslint-disable-next-line @typescript-eslint/no-empty-function 63 | public updateProjectionMatrix() {} 64 | createShaderData() { 65 | this.shaderData = new ShaderData("camera", 0, 1, 1); 66 | const uniformBuffer = new UniformBuffer({ label: "camera" }); 67 | uniformBuffer.setUniform( 68 | "projectionMatrix", 69 | () => { 70 | return this.projectionMatrix; 71 | }, 72 | UniformEnum.Mat4 73 | ); 74 | uniformBuffer.setUniform( 75 | "viewMatrix", 76 | () => { 77 | return this.viewMatrix; 78 | }, 79 | UniformEnum.Mat4 80 | ); 81 | uniformBuffer.setUniform( 82 | "inverseViewMatrix", 83 | () => { 84 | return this.inverseViewMatrix; 85 | }, 86 | UniformEnum.Mat4 87 | ); 88 | uniformBuffer.setUniform( 89 | "position", 90 | () => { 91 | return this.position; 92 | }, 93 | UniformEnum.FloatVec3 94 | ); 95 | this.shaderData.setUniformBuffer("camera", uniformBuffer); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/core/Frustum.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import Matrix4 from "../math/Matrix4"; 3 | import Plane from "../math/Plane"; 4 | import Vector3 from "../math/Vector3"; 5 | import defined from "../utils/defined"; 6 | import BoundingBox from "./BoundingBox"; 7 | import BoundingSphere from "./BoundingSphere"; 8 | import { Intersect } from "./WebGPUConstant"; 9 | 10 | export class Frustum { 11 | private _planes: Plane[]; 12 | constructor() { 13 | this._planes = []; 14 | } 15 | get planes(): Array { 16 | return this._planes; 17 | } 18 | update(camera: Camera) { 19 | const { viewMatrix, projectionMatrix } = camera; 20 | const cloneViewMatrix = viewMatrix.clone(new Matrix4()); 21 | const vpMatrix = Matrix4.multiply(projectionMatrix, cloneViewMatrix, new Matrix4()); 22 | const planes = this._planes; 23 | const me = vpMatrix; 24 | const me0 = me[0], 25 | me1 = me[1], 26 | me2 = me[2], 27 | me3 = me[3]; 28 | const me4 = me[4], 29 | me5 = me[5], 30 | me6 = me[6], 31 | me7 = me[7]; 32 | const me8 = me[8], 33 | me9 = me[9], 34 | me10 = me[10], 35 | me11 = me[11]; 36 | const me12 = me[12], 37 | me13 = me[13], 38 | me14 = me[14], 39 | me15 = me[15]; 40 | planes[0] = new Plane(new Vector3(me3 - me0, me7 - me4, me11 - me8), me15 - me12); 41 | planes[0].normalize(); 42 | planes[1] = new Plane(new Vector3(me3 + me0, me7 + me4, me11 + me8), me15 + me12); 43 | planes[1].normalize(); 44 | 45 | planes[2] = new Plane(new Vector3(me3 + me1, me7 + me5, me11 + me9), me15 + me13); 46 | planes[2].normalize(); 47 | 48 | planes[3] = new Plane(new Vector3(me3 - me1, me7 - me5, me11 - me9), me15 - me13); 49 | planes[3].normalize(); 50 | 51 | planes[4] = new Plane(new Vector3(me3 - me2, me7 - me6, me11 - me10), me15 - me14); 52 | planes[4].normalize(); 53 | 54 | planes[5] = new Plane(new Vector3(me3 + me2, me7 + me6, me11 + me10), me15 + me14); 55 | planes[5].normalize(); 56 | } 57 | computeVisibility(boundingVolume: BoundingBox | BoundingSphere) { 58 | if (!defined(boundingVolume)) { 59 | throw new Error("boundingVolume is required."); 60 | } 61 | const planes = this.planes; 62 | let intersecting = false; 63 | for (let k = 0, len = planes.length; k < len; ++k) { 64 | const result = boundingVolume.intersectPlane(planes[k]); 65 | if (result === Intersect.OUTSIDE) { 66 | return Intersect.OUTSIDE; 67 | } else if (result === Intersect.INTERSECTING) { 68 | intersecting = true; 69 | } 70 | } 71 | 72 | return intersecting ? Intersect.INTERSECTING : Intersect.INSIDE; 73 | } 74 | destroy() { 75 | this._planes = null; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /example/geometry/box.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | box 11 | 12 | 38 | 39 | 40 | 41 | 42 |
43 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/post-process/PostEffect.ts: -------------------------------------------------------------------------------- 1 | import IClone from "../core/IClone"; 2 | import { RenderObjectType } from "../core/WebGPUTypes"; 3 | import Geometry from "../geometry/Geometry"; 4 | import { Mesh } from "../mesh/Mesh"; 5 | import { Float32Attribute } from "../render/Attribute"; 6 | import Context from "../render/Context"; 7 | import { Primitive, RenderState, Target } from "../render/RenderState"; 8 | import RenderTarget from "../render/RenderTarget"; 9 | import Sampler from "../render/Sampler"; 10 | import Texture from "../render/Texture"; 11 | 12 | export default class PostEffect implements IClone { 13 | width: number; 14 | 15 | height: number; 16 | 17 | defaultSampler: Sampler; 18 | 19 | currentRenderTarget: RenderTarget; 20 | 21 | fullScreenQuad: Mesh; 22 | 23 | renderState: RenderState; 24 | 25 | id: string; 26 | 27 | priority: number; 28 | 29 | isPostEffect: boolean; 30 | 31 | type: RenderObjectType; 32 | 33 | constructor(width: number, height: number, id: string) { 34 | this.width = width; 35 | this.height = height; 36 | this.initDefaultParms(); 37 | this.id = id; 38 | this.priority = 0; 39 | this.isPostEffect = true; 40 | this.type = RenderObjectType.PostEffect; 41 | } 42 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 43 | render(context: Context, colorTexture: Texture): Texture { 44 | return null; 45 | } 46 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 47 | setSize(width: number, height: number): void { 48 | // todo ; 49 | } 50 | destroy() { 51 | this?.currentRenderTarget?.destroy(); 52 | } 53 | protected renderMesh(context: Context) { 54 | // this.fullScreenQuad.material.dirty = true; 55 | this.fullScreenQuad.material.update(); 56 | const drawComand = this.fullScreenQuad.getPassCommand(); 57 | const currentRenderPassEncoder = this.currentRenderTarget.beginRenderPass(context.device); 58 | drawComand.render({ device: context.device, passEncoder: currentRenderPassEncoder }); 59 | this.currentRenderTarget.endRenderPass(); 60 | } 61 | private initDefaultParms() { 62 | const geometry = new Geometry({}); 63 | geometry.setAttribute( 64 | new Float32Attribute("position", [-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0], 2) 65 | ); 66 | geometry.count = 6; 67 | // rs 68 | const primitive = new Primitive(); 69 | const target = new Target(); 70 | // target.format=TextureFormat.RGBA8Unorm 71 | const renderState = new RenderState(); 72 | renderState.primitive = primitive; 73 | renderState.targets = [target]; 74 | this.renderState = renderState; 75 | this.fullScreenQuad = new Mesh(geometry); 76 | 77 | this.defaultSampler = new Sampler(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/pbr/ibl.wgsl: -------------------------------------------------------------------------------- 1 | 2 | fn getLightProbeRadiance(viewDir : vec3 , normal : vec3 , roughness : f32) -> vec3 { 3 | var reflectVec : vec3 = reflect(-viewDir, normal); 4 | reflectVec.x = -reflectVec.x; //TextureCube is left-hand,so x need inverse 5 | let mipCount : f32 = 10.0;//resolution of 256x256 6 | let lod : f32 = roughness * mipCount; 7 | let specularLight : vec3 = textureSampleLevel(specularEnvTexture, specularEnvSampler, reflectVec, lod).rgb; 8 | return specularLight; 9 | } 10 | fn getLightProbeIrradiance(lightProbe : array, 9>, normal : vec3 ) -> vec3 { 11 | var worldNormal : vec3 = normal; 12 | worldNormal.x = -normal.x; 13 | var irradiance : vec3 = lightProbe[0]; 14 | irradiance += lightProbe[1] * (normal.y); 15 | irradiance += lightProbe[2] * (normal.z); 16 | irradiance += lightProbe[3] * (normal.x); 17 | 18 | irradiance += lightProbe[4] * (normal.y * normal.x); 19 | irradiance += lightProbe[5] * (normal.y * normal.z); 20 | irradiance += lightProbe[6] * (3.0 * normal.z * normal.z - 1.0); 21 | irradiance += lightProbe[7] * (normal.z * normal.x); 22 | irradiance += lightProbe[8] * (normal.x * normal.x - normal.y * normal.y); 23 | 24 | return max(irradiance, vec3 (0.0, 0.0, 0.0)); 25 | } 26 | fn DFGApprox(specularColor : vec3 , roughness : f32, dotNV : f32) -> vec3 { 27 | const c0 : vec4 = vec4 (- 1, - 0.0275, - 0.572, 0.022); 28 | let c1 : vec4 = vec4 (1, 0.0425, 1.04, - 0.04); 29 | let r : vec4 = roughness * c0 + c1; 30 | let a004 : f32 = min(r.x * r.x, exp2(- 9.28 * dotNV)) * r.x + r.y; 31 | let fab : vec2 = vec2 (- 1.04, 1.04) * a004 + r.zw; 32 | return specularColor * fab.x + fab.y; 33 | } 34 | //间接光照 35 | fn indirectDiffuse_Physical(geometry : Geometry, material : PhysicalMaterial) -> ReflectedLight { 36 | var reflectedLight : ReflectedLight; 37 | var irradiance : vec3 = lightUniforms.ambient.xyz * lightUniforms.ambient.w; 38 | irradiance *= pi; 39 | reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert(material.diffuseColor); 40 | return reflectedLight; 41 | } 42 | //间接高光 43 | fn indirectSpecular_Physical(geometry : Geometry, material : PhysicalMaterial) -> ReflectedLight { 44 | var reflectedLight : ReflectedLight; 45 | //IBL specular 46 | let radiance : vec3 = getLightProbeRadiance(geometry.viewDir, geometry.normal, material.roughness); 47 | let radianceAttenuation : f32 = 1.0; 48 | reflectedLight.indirectSpecular += radianceAttenuation * radiance * DFGApprox(material.specularColor, material.roughness, geometry.dotNV); 49 | return reflectedLight; 50 | } 51 | -------------------------------------------------------------------------------- /src/light/shadows/PointLightShadow.ts: -------------------------------------------------------------------------------- 1 | import PointLightShadowCamera from "../../camera/PointLightShadowCamera"; 2 | import Vector2 from "../../math/Vector2"; 3 | import Vector3 from "../../math/Vector3"; 4 | import Vector4 from "../../math/Vector4"; 5 | import { Light } from "../Light"; 6 | import { PointLight } from "../PointLight"; 7 | import { BaseShadow } from "./BaseShadow"; 8 | 9 | export class PointLightShadow extends BaseShadow { 10 | public type: string; 11 | private _pointLightShadowLookDirections: Array; 12 | private _pointLightShadowUps: Array; 13 | vpMatrixArrayDirty: boolean; 14 | 15 | get camera(): PointLightShadowCamera { 16 | return this._camera as PointLightShadowCamera; 17 | } 18 | 19 | constructor() { 20 | const camera = new PointLightShadowCamera(90, 1, 0.1, 500); 21 | super(new Vector2(1536, 1024), camera); 22 | this.viewportSize = new Vector2(512, 512); 23 | this.currentViewportIndex = 0; 24 | this.type = "pointLightShadow"; 25 | this.vpMatrixArrayDirty = true; 26 | 27 | this._viewports = [ 28 | // positive X 0 29 | new Vector4(0, 0, 1 / 3, 1 / 2), 30 | // negative X 1 31 | new Vector4(1, 0, 1 / 3, 1 / 2), 32 | // positive Z 2 33 | new Vector4(2, 0, 1 / 3, 1 / 2), 34 | // negative Z 3 35 | new Vector4(0, 1, 1 / 3, 1 / 2), 36 | // positive Y 4 37 | new Vector4(1, 1, 1 / 3, 1 / 2), 38 | // negative Y 5 39 | new Vector4(2, 1, 1 / 3, 1 / 2) 40 | ]; 41 | 42 | this._pointLightShadowLookDirections = [ 43 | new Vector3(1, 0, 0), 44 | new Vector3(-1, 0, 0), 45 | new Vector3(0, 0, 1), 46 | new Vector3(0, 0, -1), 47 | new Vector3(0, 1, 0), 48 | new Vector3(0, -1, 0) 49 | ]; 50 | 51 | this._pointLightShadowUps = [ 52 | new Vector3(0, 1, 0), 53 | new Vector3(0, 1, 0), 54 | new Vector3(0, 1, 0), 55 | new Vector3(0, 1, 0), 56 | new Vector3(0, 0, 1), 57 | new Vector3(0, 0, -1) 58 | ]; 59 | super.init(); 60 | } 61 | 62 | public update(light: Light) { 63 | if (light instanceof PointLight) this.updateMatrices(light); 64 | } 65 | 66 | updateMatrices(light: PointLight) { 67 | if (this.camera instanceof PointLightShadowCamera) { 68 | this.camera.position.copy(light.position); 69 | const target = Vector3.clone(light.position); 70 | target.add(this._pointLightShadowLookDirections[this.currentViewportIndex]); 71 | this.camera.up.copy(this._pointLightShadowUps[this.currentViewportIndex]); 72 | const { x, y, z } = target; 73 | this.camera.lookAt(x, y, z); 74 | this.camera.updateMatrix(); 75 | this.camera.updateVpMatrixArrayAndIndex(this.currentViewportIndex); 76 | if (this.currentViewportIndex == 5) this.vpMatrixArrayDirty = true; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/core/RenderObject.ts: -------------------------------------------------------------------------------- 1 | import Matrix4 from "../math/Matrix4"; 2 | import { Quaternion } from "../math/Quaternion"; 3 | import Vector3 from "../math/Vector3"; 4 | import IClone from "./IClone"; 5 | import { RenderObjectType } from "./WebGPUTypes"; 6 | 7 | export default class RenderObject implements IClone { 8 | public up: Vector3; 9 | protected _position: Vector3; 10 | protected _scale: Vector3; 11 | protected _quaternion: Quaternion; 12 | protected _target: Vector3; 13 | private _normalMatrix: Matrix4; 14 | modelMatrix: Matrix4; 15 | parent: RenderObject; 16 | type: RenderObjectType; 17 | name: string; 18 | castShadow: boolean; 19 | receiveShadow: boolean; 20 | 21 | constructor() { 22 | this._position = new Vector3(); 23 | this._scale = new Vector3(1, 1, 1); 24 | this._quaternion = new Quaternion(); 25 | this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY, new Matrix4()); 26 | this._normalMatrix = Matrix4.clone(Matrix4.IDENTITY, new Matrix4()); 27 | this.up = new Vector3(0, 1, 0); 28 | this._target = new Vector3(0, 0, 0); 29 | 30 | this.castShadow = false; 31 | this.receiveShadow = false; 32 | } 33 | public get normalMatrix(): Matrix4 { 34 | return this._normalMatrix; 35 | } 36 | 37 | public get position(): Vector3 { 38 | return this._position; 39 | } 40 | public get scale(): Vector3 { 41 | return this._scale; 42 | } 43 | public get quaternion(): Quaternion { 44 | return this._quaternion; 45 | } 46 | private updateNormalMatrix() { 47 | Matrix4.inverse(this.modelMatrix, this._normalMatrix); 48 | Matrix4.transpose(this._normalMatrix, this._normalMatrix); 49 | } 50 | updateMatrix(matrix?: Matrix4) { 51 | this.modelMatrix.compose(this.position, this.quaternion, this.scale); 52 | if (matrix) Matrix4.multiply(matrix, this.modelMatrix, this.modelMatrix); 53 | this.updateNormalMatrix(); 54 | } 55 | lookAt(x, y, z) { 56 | this._target.set(x, y, z); 57 | if (this.type == RenderObjectType.Camera || this.type == RenderObjectType.Light) { 58 | _m1.lookAt(this.position, this._target, this.up); 59 | } else { 60 | _m1.lookAt(this._target, this.position, this.up); 61 | } 62 | this.quaternion.setFromRotationMatrix(_m1); 63 | } 64 | rotateOnAxis(axis, angle) { 65 | const quat = Quaternion.fromAxisAngle(axis, angle); 66 | Quaternion.multiply(this.quaternion, quat, this.quaternion); 67 | } 68 | rotateX(angle) { 69 | return this.rotateOnAxis(_xAxis, angle); 70 | } 71 | rotateY(angle) { 72 | return this.rotateOnAxis(_yAxis, angle); 73 | } 74 | rotateZ(angle) { 75 | return this.rotateOnAxis(_zAxis, angle); 76 | } 77 | } 78 | const _xAxis = new Vector3(1, 0, 0); 79 | const _yAxis = new Vector3(0, 1, 0); 80 | const _zAxis = new Vector3(0, 0, 1); 81 | const _m1 = new Matrix4(); 82 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/pbr/pbrStruct.wgsl: -------------------------------------------------------------------------------- 1 | struct MaterialUniform{ 2 | 3 | modelMatrix : mat4x4 , 4 | 5 | diffuse : vec3 , 6 | 7 | opacity : f32, 8 | 9 | normalMatrix : mat3x3 , 10 | 11 | emissive : vec3 , 12 | 13 | roughness : f32, 14 | 15 | metalness : f32, 16 | 17 | #if TONE_MAPPING 18 | toneMappingExposure : f32, 19 | #endif 20 | 21 | #if SPECULAR 22 | 23 | specularColor : vec3 , 24 | 25 | specularIntensity : f32, 26 | #endif 27 | 28 | #if USE_SHEEN 29 | 30 | sheenColor : vec3 , 31 | 32 | sheenRoughness : f32, 33 | #endif 34 | 35 | #if USE_TRANSMISSION 36 | 37 | attenuationColor : vec3 , 38 | 39 | transmission : f32, 40 | 41 | transmissionSamplerSize : vec2 , 42 | 43 | thickness : f32, 44 | 45 | attenuationDistance : f32, 46 | 47 | #endif 48 | 49 | #if USE_SKINNING 50 | 51 | bindMatrix : mat4x4 , 52 | 53 | bindMatrixInverse : mat4x4 , 54 | 55 | boneTextureSize : u32, 56 | #endif 57 | 58 | #if USE_NORMALTEXTURE 59 | normalScale : vec2 , 60 | #endif 61 | 62 | #if IOR 63 | ior : f32, 64 | #endif 65 | 66 | #if USE_CLEARCOAT 67 | 68 | #if USE_CLEARCOAT_NORMALTEXTURE 69 | clearcoatNormalScale : vec2 , 70 | #endif 71 | 72 | clearcoat : f32, 73 | 74 | clearcoatRoughness : f32, 75 | #endif 76 | 77 | #if USE_IRIDESCENCE 78 | iridescence : f32, 79 | 80 | iridescenceIOR : f32, 81 | 82 | iridescenceThicknessMinimum : f32, 83 | 84 | iridescenceThicknessMaximum : f32, 85 | 86 | #endif 87 | 88 | #if USE_AOTEXTURE 89 | aoTextureIntensity : f32, 90 | #endif 91 | 92 | #if USE_LIGHTTEXTURE 93 | lightTextureIntensity : f32, 94 | #endif 95 | 96 | #if USE_ENVTEXTURE 97 | envTextureIntensity : f32, 98 | 99 | flipEnvTexture : f32, 100 | #endif 101 | 102 | #if USE_BUMPTEXTURE 103 | bumpScale : f32; 104 | #endif 105 | 106 | #if USE_DISPLACEMENTTEXTURE 107 | 108 | displacementScale : f32, 109 | 110 | displacementBias : f32, 111 | #endif 112 | 113 | #if USE_MORPHTARGETS 114 | 115 | morphTargetBaseInfluence : f32, 116 | 117 | #if MORPHTARGETS_TEXTURE 118 | 119 | morphTargetsTextureSize : vec2 < u32>, 120 | 121 | MORPHTARGETS_COUNT : u32, 122 | 123 | #endif 124 | 125 | morphTargetInfluences : array, 126 | 127 | #endif 128 | } 129 | -------------------------------------------------------------------------------- /example/mesh/skybox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | skybox 11 | 12 | 38 | 39 | 40 | 41 |
42 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/render/RenderTarget.ts: -------------------------------------------------------------------------------- 1 | import { PassType } from "../core/WebGPUTypes"; 2 | import Attachment from "./Attachment"; 3 | import QuerySet from "./QuerySet"; 4 | import Texture from "./Texture"; 5 | 6 | export default class RenderTarget { 7 | public device: GPUDevice; 8 | public commandEncoder: GPUCommandEncoder | null; 9 | private _renderPassDescriptor: GPURenderPassDescriptor; 10 | private renderEncoder: GPURenderPassEncoder; 11 | constructor( 12 | public type: PassType, 13 | public colorAttachments: Attachment[], 14 | public depthAttachment?: Attachment, 15 | public stencilAttachment?: Attachment, 16 | public querySet?: QuerySet, 17 | public fixedSize?: boolean 18 | ) { 19 | this.renderEncoder = undefined; 20 | this._renderPassDescriptor = undefined; 21 | this.commandEncoder = undefined; 22 | this.device = undefined; 23 | this.fixedSize = false; 24 | } 25 | get renderPassDescriptor() { 26 | this._renderPassDescriptor = this.getRenderPassDescriptor(); 27 | return this._renderPassDescriptor; 28 | } 29 | public getColorTexture(index = 0): Texture { 30 | const colAtt = this.colorAttachments[index]; 31 | return colAtt?.texture ?? null; 32 | } 33 | public getDepthTexture(): Texture { 34 | return this?.depthAttachment?.texture; 35 | } 36 | private getRenderPassDescriptor(): GPURenderPassDescriptor | null { 37 | this?.querySet?.update(this.device); 38 | return { 39 | colorAttachments: this?.colorAttachments?.map?.((colorAttachment) => 40 | colorAttachment.getGPURenderPassColorAttachment(this.device) 41 | ), 42 | depthStencilAttachment: Object.assign( 43 | {}, 44 | this?.depthAttachment?.getGPURenderPassDepthAttachment?.(this.device), 45 | this?.stencilAttachment?.getGPURenderPassStencilAttachment?.(this.device) 46 | ), 47 | ...(this.querySet && { occlusionQuerySet: this.querySet.gpuQuerySet }) 48 | }; 49 | } 50 | 51 | public beginRenderPass(device: GPUDevice) { 52 | if (!this.device) this.device = device; 53 | this.commandEncoder = this.device.createCommandEncoder(); 54 | this.renderEncoder = this.commandEncoder.beginRenderPass(this.renderPassDescriptor); 55 | return this.renderEncoder; 56 | } 57 | public endRenderPass() { 58 | this.renderEncoder?.end(); 59 | this.device.queue.submit([this.commandEncoder.finish()]); 60 | this.commandEncoder = null; 61 | this.renderEncoder = null; 62 | } 63 | public setSize(width: number, height: number, depth = 1) { 64 | if (this.fixedSize) return; 65 | this?.depthAttachment?.texture?.setSize?.(width, height, depth); 66 | this?.colorAttachments?.forEach?.((colorAttachment) => 67 | colorAttachment?.texture?.setSize?.(width, height, depth) 68 | ); 69 | } 70 | destroy() { 71 | this?.colorAttachments?.forEach?.((colorAttachment) => colorAttachment?.texture?.destroy?.()); 72 | this?.depthAttachment?.texture?.destroy(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/shader/shaderChunk/pbr/pbrUtils.wgsl: -------------------------------------------------------------------------------- 1 | const pi : f32 = 3.141592653589793; 2 | 3 | const reciprocal_pi : f32 = 0.3183098861837907; 4 | fn pow2(x : f32) -> f32 { 5 | return x * x; 6 | } 7 | fn pow2Vector(x : vec3 ) -> vec3 { 8 | return x * x; 9 | } 10 | fn pow3(x : f32) -> f32 { 11 | return x * x*x; 12 | } 13 | fn pow4(x : f32) -> f32 { 14 | let x2 : f32 = x * x; 15 | return x2 * x2; 16 | } 17 | fn max3(v : vec3 ) -> f32 { 18 | return max(max(v.x, v.y), v.z); 19 | } 20 | fn average(v : vec3 ) -> f32 { 21 | return dot(v, vec3 (0.3333333) ); 22 | } 23 | fn rand(uv : vec2 ) -> f32 { 24 | let a : f32 = 12.9898; 25 | let b : f32 = 78.233; 26 | let c : f32 = 43758.5453; 27 | let dt : f32 = dot(uv.xy, vec2 (a, b) ); 28 | let sn : f32 = dt % pi; 29 | return fract(sin(sn) * c); 30 | } 31 | fn transformDirection(dir : vec3 , matrix : mat4x4 ) -> vec3 { 32 | return normalize((matrix * vec4 (dir, 0.0) ).xyz); 33 | } 34 | 35 | fn transposeMat3(m : mat3x3 ) -> mat3x3 { 36 | var tmp : mat3x3 ; 37 | tmp[ 0 ] = vec3 (m[ 0 ].x, m[ 1 ].x, m[ 2 ].x); 38 | tmp[ 1 ] = vec3 (m[ 0 ].y, m[ 1 ].y, m[ 2 ].y); 39 | tmp[ 2 ] = vec3 (m[ 0 ].z, m[ 1 ].z, m[ 2 ].z); 40 | return tmp; 41 | } 42 | fn luminance(rgb : vec3 ) -> f32 { 43 | let weights : vec3 = vec3 (0.2126729, 0.7151522, 0.0721750); 44 | return dot(weights, rgb); 45 | } 46 | fn LinearToneMapping(color : vec3 , toneMappingExposure : f32) -> vec3 { 47 | return toneMappingExposure * color; 48 | } 49 | 50 | fn ReinhardToneMapping(color : vec3 , toneMappingExposure : f32) -> vec3 { 51 | var tempColor : vec3 ; 52 | tempColor = color; 53 | tempColor *= toneMappingExposure; 54 | return saturate(tempColor / (vec3 (1.0) + tempColor) ); 55 | } 56 | fn CustomToneMapping(color : vec3 ) -> vec3 { 57 | return color; 58 | } 59 | fn toneMapping(color : vec3 , toneMappingExposure : f32) -> vec3 { 60 | return ReinhardToneMapping(color, toneMappingExposure); 61 | } 62 | 63 | fn LinearToLinear(value : vec4 ) -> vec4 { 64 | return value; 65 | } 66 | fn lessThanEqual(a : vec3 , b : vec3 ) -> vec3 { 67 | let xValue : f32 = select(b.x, a.x, a.x <= b.x); 68 | let yValue : f32 = select(b.y, a.y, a.y <= b.y); 69 | let zValue : f32 = select(b.z, a.z, a.z <= b.z); 70 | return vec3 (xValue, yValue, zValue); 71 | } 72 | fn LinearTosRGB(value : vec4 ) -> vec4 { 73 | return vec4 (mix(pow(value.rgb, vec3 (0.41666) ) * 1.055 - vec3 (0.055), value.rgb * 12.92, vec3 (lessThanEqual(value.rgb, vec3 (0.0031308) )) ), value.a); 74 | } 75 | fn linearToOutputTexel(value : vec4 ) -> vec4 { 76 | return LinearTosRGB(value); 77 | } 78 | -------------------------------------------------------------------------------- /example/light/ambient.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | ambient 11 | 12 | 38 | 39 | 40 | 41 | 42 |
43 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/pass/BasicPass.ts: -------------------------------------------------------------------------------- 1 | import Camera from "../camera/Camera"; 2 | import { FrameState } from "../core/FrameState"; 3 | import { TextureFormat, TextureUsage } from "../core/WebGPUConstant"; 4 | import { Pass } from "../core/WebGPUTypes"; 5 | import Color from "../math/Color"; 6 | import Attachment from "../render/Attachment"; 7 | import Context from "../render/Context"; 8 | import DrawCommand from "../render/DrawCommand"; 9 | import RenderTarget from "../render/RenderTarget"; 10 | import Texture from "../render/Texture"; 11 | import RenderPass from "./RenderPass"; 12 | 13 | export class BasicPass extends RenderPass { 14 | skyboxDrawComand: DrawCommand; 15 | constructor(context: Context) { 16 | super(context); 17 | this.init(context); 18 | } 19 | 20 | beforeRender(frameState: FrameState) { 21 | this.updateRenderTarget(frameState); 22 | 23 | super.beforeRender(); 24 | } 25 | 26 | render(frameState: FrameState, camera?: Camera) { 27 | const { renderQueue, lightManger } = frameState; 28 | 29 | renderQueue.sort(); 30 | renderQueue.preRender(camera, this.context, this.passRenderEncoder); 31 | renderQueue.opaqueRender(camera, this.context, this.passRenderEncoder, lightManger, Pass.RENDER); 32 | renderQueue.transparentRender(camera, this.context, this.passRenderEncoder, lightManger, Pass.RENDER); 33 | renderQueue.debugQueueRender(camera, this.context, this.passRenderEncoder); 34 | } 35 | private init(context: Context) { 36 | this.createRenderTarget(context); 37 | } 38 | private createRenderTarget(context: Context) { 39 | const { width, height, depth } = context.presentationSize; 40 | const colorTexture = new Texture({ 41 | label: "basicPassColor", 42 | textureDescriptor: { 43 | size: { width, height, depth }, 44 | format: this.context.presentationFormat, 45 | usage: TextureUsage.RenderAttachment | TextureUsage.TextureBinding 46 | } 47 | }); 48 | const depthTexture = new Texture({ 49 | label: "basicPassDepth", 50 | textureDescriptor: { 51 | size: { width, height, depth }, 52 | format: TextureFormat.Depth24Plus, 53 | usage: TextureUsage.RenderAttachment 54 | } 55 | }); 56 | const colorAttachment = new Attachment({ r: 0.0, g: 0.0, b: 0.0, a: 0.0 }, { texture: colorTexture }); 57 | const depthAttachment = new Attachment(1.0, { texture: depthTexture }); 58 | this.renderTarget = new RenderTarget("render", [colorAttachment], depthAttachment); 59 | } 60 | public setSize(width: number, height: number): void { 61 | this.renderTarget.setSize(width, height, 1); 62 | } 63 | private updateRenderTarget(frameState: FrameState) { 64 | if (frameState?.background?.value instanceof Color) { 65 | const { red, green, blue } = frameState.background.value; 66 | const opacity = frameState.background?.opacity; 67 | const clearValue = { 68 | r: red, 69 | g: green, 70 | b: blue, 71 | a: opacity ?? 1.0 72 | }; 73 | this.renderTarget.colorAttachments[0].value = clearValue; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/render/VertexBuffer.ts: -------------------------------------------------------------------------------- 1 | import { InputStepMode } from "../core/WebGPUConstant"; 2 | import { VertexBufferParams } from "../core/WebGPUTypes"; 3 | import { Attribute, AttributeType, InterleavedAttribute, InterleavedFloat32Attribute } from "./Attribute"; 4 | import Attributes from "./Attributes"; 5 | import Buffer from "./Buffer"; 6 | export default class VertexBuffer { 7 | public index: number; 8 | public arrayStride: number; 9 | public stepMode: string; 10 | public buffer: Buffer; 11 | public attributes: Attributes; 12 | public dirty: boolean; 13 | public defines: { [prop: string]: boolean | number }; 14 | public locationIndex: number; 15 | private label: string; 16 | // 17 | constructor(params: VertexBufferParams) { 18 | const { label, index, locationIndex = 0, stepMode = InputStepMode.Vertex, arrayStride } = params; 19 | this.index = index || 0; 20 | this.attributes = new Attributes(locationIndex); 21 | this.stepMode = stepMode; 22 | this.dirty = true; 23 | this.label = label?.concat(`_${index}_VertexBuffer`); 24 | this.arrayStride = arrayStride; 25 | this.defines = {}; 26 | this.locationIndex = locationIndex; 27 | } 28 | public getBufferDes() { 29 | const result = { 30 | arrayStride: this.arrayStride, 31 | stepMode: this.stepMode, 32 | attributes: this.attributes.getGPUAttributesDes() 33 | }; 34 | return result; 35 | } 36 | public setAttribute(attribute: Attribute | InterleavedAttribute) { 37 | if (attribute.attributeType === AttributeType.attribute) { 38 | this.setLocationIndex((attribute as Attribute).name); 39 | } else { 40 | (attribute as InterleavedFloat32Attribute)?.names.forEach((name: string) => this.setLocationIndex(name)); 41 | } 42 | this.attributes.setAttribute(attribute); 43 | this.dirty = true; 44 | } 45 | public getAttribute(name: string): Attribute | InterleavedAttribute { 46 | return this.attributes.getAttribute(name); 47 | } 48 | public containAttribute(name: string): boolean { 49 | return this.defines[name?.concat("Location")] != undefined ? true : false; 50 | } 51 | public bind(device: GPUDevice, passEncoder: GPURenderPassEncoder) { 52 | if (this.attributes.dirty) { 53 | this.attributes.dirty = false; 54 | const { arrayStride, typeArray, buffer } = this.attributes.getAtrributeValues(); 55 | if (this.arrayStride === undefined) this.arrayStride = arrayStride; 56 | if (!this.buffer) { 57 | this.buffer = buffer ?? Buffer.createVertexBuffer(this.label, device, typeArray); 58 | } else { 59 | // update Buffer 60 | if (typeArray) this.buffer.setSubData(0, typeArray); 61 | } 62 | } 63 | passEncoder.setVertexBuffer(this.index, this.buffer.gpuBuffer); 64 | } 65 | private setLocationIndex(name: string) { 66 | if (this.defines[name?.concat("Location")] || !name) return; 67 | this.defines[name?.concat("Location")] = this.locationIndex; 68 | this.locationIndex += 1; 69 | } 70 | destroy() { 71 | this.buffer.destroy(); 72 | this.attributes.destroy(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /example/light/spotLight.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | webworkerTask 11 | 12 | 38 | 39 | 40 | 41 | 42 |
43 | 92 | 93 | 94 | --------------------------------------------------------------------------------