Shader code beginnings
This commit is contained in:
		| @@ -127,6 +127,25 @@ export class Matrix4x4 { | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	static fromTRS(t: Vector3Object, r: QuaternionObject, s: Vector3Object): Matrix4x4 { | ||||
| 		const xx = r.x * r.x; | ||||
| 		const xy = r.x * r.y; | ||||
| 		const xz = r.x * r.z; | ||||
| 		const xw = r.x * r.w; | ||||
| 		const yy = r.y * r.y; | ||||
| 		const yz = r.y * r.z; | ||||
| 		const yw = r.y * r.w; | ||||
| 		const zz = r.z * r.z; | ||||
| 		const zw = r.z * r.w; | ||||
|  | ||||
| 		return new Matrix4x4( | ||||
| 			s.x * (1 - 2 * (yy + zz)), s.x * 2 * (xy + zw), s.x * 2 * (xz - yw), 0, | ||||
| 			s.y * 2 * (xy - zw), s.y * (1 - 2 * (xx + zz)), s.y * 2 * (yz + xw), 0, | ||||
| 			s.z * 2 * (xz + yw), s.z * 2 * (yz - xw), s.z * (1 - 2 * (xx + yy)), 0, | ||||
| 			t.x, t.y, t.z, 1, | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	setObject(object: Matrix4x4Object): Matrix4x4 { | ||||
| 		this.ix = object.ix; | ||||
| 		this.iy = object.iy; | ||||
| @@ -236,6 +255,61 @@ export class Matrix4x4 { | ||||
| 		this.tw = 1; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	setTRS(t: Vector3Object, r: QuaternionObject, s: Vector3Object): Matrix4x4 { | ||||
| 		const xx = r.x * r.x; | ||||
| 		const xy = r.x * r.y; | ||||
| 		const xz = r.x * r.z; | ||||
| 		const xw = r.x * r.w; | ||||
| 		const yy = r.y * r.y; | ||||
| 		const yz = r.y * r.z; | ||||
| 		const yw = r.y * r.w; | ||||
| 		const zz = r.z * r.z; | ||||
| 		const zw = r.z * r.w; | ||||
|  | ||||
| 		this.ix = s.x * (1 - 2 * (yy + zz)); | ||||
| 		this.iy = s.x * 2 * (xy + zw); | ||||
| 		this.iz = s.x * 2 * (xz - yw); | ||||
| 		this.iw = 0; | ||||
| 		this.jx = s.y * 2 * (xy - zw); | ||||
| 		this.jy = s.y * (1 - 2 * (xx + zz)); | ||||
| 		this.jz = s.y * 2 * (yz + xw); | ||||
| 		this.jw = 0; | ||||
| 		this.kx = s.z * 2 * (xz + yw); | ||||
| 		this.ky = s.z * 2 * (yz - xw); | ||||
| 		this.kz = s.z * (1 - 2 * (xx + yy)); | ||||
| 		this.kw = 0; | ||||
| 		this.tx = t.x; | ||||
| 		this.ty = t.y; | ||||
| 		this.tz = t.z; | ||||
| 		this.tw = 1; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	add(m: Matrix4x4): Matrix4x4 { | ||||
| 		throw new Error("TODO"); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	sub(m: Matrix4x4): Matrix4x4 { | ||||
| 		throw new Error("TODO"); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	mulScalar(k: number): Matrix4x4 { | ||||
| 		throw new Error("TODO"); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	mulMatrix(m: Matrix4x4): Matrix4x4 { | ||||
| 		throw new Error("TODO"); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	premulMatrix(m: Matrix4x4): Matrix4x4 { | ||||
| 		throw new Error("TODO"); | ||||
| 		return this; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| Object.defineProperty(Matrix4x4.prototype, "type", { value: "Matrix4x4" }); | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|  * obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
|  | ||||
| import { Camera, Mesh, Quaternion, QuaternionObject, Vector3, Vector3Object } from "."; | ||||
| import { Camera, Matrix4x4, Mesh, Quaternion, QuaternionObject, Vector3, Vector3Object } from "."; | ||||
| import { Material } from "../resources"; | ||||
|  | ||||
| export interface NodeProps { | ||||
| @@ -44,6 +44,9 @@ export class Node { | ||||
| 	/** backreference */ | ||||
| 	_parent: Node | null; | ||||
|  | ||||
| 	_localMatrix: Matrix4x4; | ||||
| 	_worldMatrix: Matrix4x4; | ||||
|  | ||||
| 	constructor({ | ||||
| 		name = "", | ||||
| 		translation, | ||||
| @@ -68,6 +71,9 @@ export class Node { | ||||
|  | ||||
| 		this._parent = null; | ||||
|  | ||||
| 		this._localMatrix = Matrix4x4.fromTRS(this._translation, this._rotation, this._scale); | ||||
| 		this._worldMatrix = Matrix4x4.fromObject(this._localMatrix); | ||||
|  | ||||
| 		if (this._camera !== null) { | ||||
| 			this._camera._node = this; | ||||
| 		} | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|  * obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
|  | ||||
| import { Camera, Scene } from "./data"; | ||||
| import { IndexBuffer, IndexBufferProps, Material, MaterialProps, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps } from "./resources"; | ||||
|  | ||||
| export class Renderer { | ||||
| @@ -20,6 +21,8 @@ export class Renderer { | ||||
| 	/** 1×1 rgba8unorm texture of [128, 128, 128, 255] */ | ||||
| 	_textureNormal: Texture2D; | ||||
|  | ||||
| 	_depthBuffer: Texture2D; | ||||
|  | ||||
| 	/** | ||||
| 	 * This constructor is intended primarily for internal use. Consider using | ||||
| 	 * `Renderer.createIndexBuffer` instead. | ||||
| @@ -36,6 +39,7 @@ export class Renderer { | ||||
| 		this._format = format; | ||||
|  | ||||
| 		this._textureWhite = new Texture2D(this, { | ||||
| 			name: "White", | ||||
| 			width: 1, | ||||
| 			height: 1, | ||||
| 			format: "linear", | ||||
| @@ -43,6 +47,7 @@ export class Renderer { | ||||
| 		this._textureWhite.writeFull(new Uint8Array([255, 255, 255, 255])); | ||||
|  | ||||
| 		this._textureBlack = new Texture2D(this, { | ||||
| 			name: "Black", | ||||
| 			width: 1, | ||||
| 			height: 1, | ||||
| 			format: "linear", | ||||
| @@ -50,11 +55,20 @@ export class Renderer { | ||||
| 		this._textureBlack.writeFull(new Uint8Array([0, 0, 0, 255])); | ||||
|  | ||||
| 		this._textureNormal = new Texture2D(this, { | ||||
| 			name: "Normal", | ||||
| 			width: 1, | ||||
| 			height: 1, | ||||
| 			format: "linear", | ||||
| 		}); | ||||
| 		this._textureNormal.writeFull(new Uint8Array([128, 128, 128, 255])); | ||||
|  | ||||
| 		const framebufferTexture = this._context.getCurrentTexture(); | ||||
| 		this._depthBuffer = new Texture2D(this, { | ||||
| 			name: "Depth Buffer", | ||||
| 			width: framebufferTexture.width, | ||||
| 			height: framebufferTexture.height, | ||||
| 			format: "depth", | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	static async init(canvas: HTMLCanvasElement) { | ||||
| @@ -90,6 +104,7 @@ export class Renderer { | ||||
| 		this._textureWhite.dispose(); | ||||
| 		this._textureBlack.dispose(); | ||||
| 		this._textureNormal.dispose(); | ||||
| 		this._depthBuffer.dispose(); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| @@ -108,4 +123,36 @@ export class Renderer { | ||||
| 	createVertexBuffer(props: VertexBufferProps): VertexBuffer { | ||||
| 		return new VertexBuffer(this, props); | ||||
| 	} | ||||
|  | ||||
| 	render(scene: Scene, camera: Camera): Renderer { | ||||
| 		const { width, height } = this._context.getCurrentTexture(); | ||||
| 		if (this._depthBuffer.width !== width || this._depthBuffer.height !== height) { | ||||
| 			this._depthBuffer.resizeDiscard({ | ||||
| 				width, | ||||
| 				height, | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		const encoder = this._device.createCommandEncoder(); | ||||
|  | ||||
| 		const pass = encoder.beginRenderPass({ | ||||
| 			colorAttachments: [{ | ||||
| 				view: this._context.getCurrentTexture().createView(), | ||||
| 				loadOp: "clear", | ||||
| 				storeOp: "store", | ||||
| 			}], | ||||
| 			depthStencilAttachment: { | ||||
| 				view: this._depthBuffer._textureView, | ||||
| 				depthClearValue: 0, | ||||
| 				depthLoadOp: "clear", | ||||
| 				depthStoreOp: "store", | ||||
| 			}, | ||||
| 		}); | ||||
| 		pass.end(); | ||||
|  | ||||
| 		const commandBuffer = encoder.finish(); | ||||
| 		this._device.queue.submit([commandBuffer]); | ||||
|  | ||||
| 		return this; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -15,17 +15,18 @@ export interface MaterialProps { | ||||
|  | ||||
| 	baseColor?: ColorObject; | ||||
| 	partialCoverage?: number; | ||||
| 	occlusionTextureStrength?: number; | ||||
| 	metallic?: number; | ||||
| 	roughness?: number; | ||||
| 	normalScale?: number; | ||||
| 	emissive?: ColorObject; | ||||
| 	transmission?: ColorObject; | ||||
| 	collimation?: number; | ||||
| 	occlusionTextureStrength?: number; | ||||
| 	roughness?: number; | ||||
| 	metallic?: number; | ||||
| 	normalScale?: number; | ||||
| 	emissive?: ColorObject; | ||||
| 	ior?: number; | ||||
|  | ||||
| 	baseColorPartialCoverageTexture?: Texture2D | null; | ||||
| 	occlusionMetallicRoughnessTexture?: Texture2D | null; | ||||
| 	occlusionTexture?: Texture2D | null; | ||||
| 	metallicRoughnessTexture?: Texture2D | null; | ||||
| 	normalTexture?: Texture2D | null; | ||||
| 	emissiveTexture?: Texture2D | null; | ||||
| 	transmissionCollimationTexture?: Texture2D | null; | ||||
| @@ -53,7 +54,8 @@ export class Material { | ||||
| 	_ior: number; | ||||
|  | ||||
| 	_baseColorPartialCoverageTexture: Texture2D | null; | ||||
| 	_occlusionMetallicRoughnessTexture: Texture2D | null; | ||||
| 	_occlusionTexture: Texture2D | null; | ||||
| 	_metallicRoughnessTexture: Texture2D | null; | ||||
| 	_normalTexture: Texture2D | null; | ||||
| 	_emissiveTexture: Texture2D | null; | ||||
| 	_transmissionCollimationTexture: Texture2D | null; | ||||
| @@ -74,7 +76,8 @@ export class Material { | ||||
| 		collimation = 1, | ||||
| 		ior = 1.45, | ||||
| 		baseColorPartialCoverageTexture = null, | ||||
| 		occlusionMetallicRoughnessTexture = null, | ||||
| 		occlusionTexture = null, | ||||
| 		metallicRoughnessTexture = null, | ||||
| 		normalTexture = null, | ||||
| 		emissiveTexture = null, | ||||
| 		transmissionCollimationTexture = null, | ||||
| @@ -97,7 +100,8 @@ export class Material { | ||||
| 		this._ior = ior; | ||||
|  | ||||
| 		this._baseColorPartialCoverageTexture = baseColorPartialCoverageTexture; | ||||
| 		this._occlusionMetallicRoughnessTexture = occlusionMetallicRoughnessTexture; | ||||
| 		this._occlusionTexture = occlusionTexture; | ||||
| 		this._metallicRoughnessTexture = metallicRoughnessTexture; | ||||
| 		this._normalTexture = normalTexture; | ||||
| 		this._emissiveTexture = emissiveTexture; | ||||
| 		this._transmissionCollimationTexture = transmissionCollimationTexture; | ||||
|   | ||||
							
								
								
									
										136
									
								
								src/shader.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/shader.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| export interface ShaderFlags { | ||||
| 	texCoord: boolean; | ||||
| 	lightTexCoord: boolean; | ||||
| 	normal: boolean; | ||||
| 	tangent: boolean; | ||||
| } | ||||
|  | ||||
| export function createShaderCode({ | ||||
| 	texCoord, | ||||
| 	lightTexCoord, | ||||
| 	normal, | ||||
| 	tangent, | ||||
| }: ShaderFlags): string { | ||||
| 	let vertexLocation = 0; | ||||
| 	let varyingLocation = 0; | ||||
|  | ||||
| 	return ` | ||||
| struct Vertex { | ||||
| 	@location(${vertexLocation++}) positionOS: vec3<f32>, | ||||
| 	${texCoord ? `@location(${vertexLocation++}) texCoord: vec2<f32>,` : ""} | ||||
| 	${lightTexCoord ? `@location(${vertexLocation++}) lightTexCoord: vec2<f32>,` : ""} | ||||
| 	${normal ? `@location(${vertexLocation++}) normalOS: vec3<f32>,` : ""} | ||||
| 	${normal && tangent ? `@location(${vertexLocation++}) tangentOS: vec4<f32>,` : ""} | ||||
| } | ||||
|  | ||||
| struct Varyings { | ||||
| 	@builtin(position) positionCS: vec4<f32>, | ||||
| 	@location(${varyingLocation++}) positionVS: vec4<f32>, | ||||
| 	${texCoord ? `@location(${varyingLocation++}) texCoord: vec2<f32>,` : ""} | ||||
| 	${lightTexCoord ? `@location(${varyingLocation++}) lightTexCoord: vec2<f32>,` : ""} | ||||
| 	${normal ? `@location(${varyingLocation++}) normalVS: vec3<f32>,` : ""} | ||||
| 	${normal && tangent ? `@location(${varyingLocation++}) tangentVS: vec3<f32>,` : ""} | ||||
| 	${normal && tangent ? `@location(${varyingLocation++}) bitangentVS: vec3<f32>,` : ""} | ||||
| } | ||||
|  | ||||
| struct PointLight { | ||||
| 	positionWS: vec3<f32>, | ||||
| 	color: vec3<f32>, | ||||
| } | ||||
|  | ||||
| struct DirectionalLight { | ||||
| 	directionWS: vec3<f32>, | ||||
| 	color: vec3<f32>, | ||||
| } | ||||
|  | ||||
| struct GlobalUniforms { | ||||
| 	matrixWStoVS: mat4x4<f32>, | ||||
| 	matrixVStoCS: mat4x4<f32>, | ||||
| 	ambientLight: vec3<f32>, | ||||
| 	pointLightCount: u32, | ||||
| 	directionalLightCount: u32, | ||||
| } | ||||
|  | ||||
| struct MaterialUniforms { | ||||
| 	baseColor: vec3<f32>, | ||||
| 	partialCoverage: f32, | ||||
| 	transmission: vec3<f32>, | ||||
| 	collimation: f32, | ||||
| 	occlusionTextureStrength: f32, | ||||
| 	roughness: f32, | ||||
| 	metallic: f32, | ||||
| 	normalScale: f32, | ||||
| 	emissive: vec3<f32>, | ||||
| 	ior: f32, | ||||
| } | ||||
|  | ||||
| struct ObjectUniforms { | ||||
| 	matrixOStoWS: mat4x4<f32>, | ||||
| 	matrixOStoWSNormal: mat4x4<f32>, | ||||
| } | ||||
|  | ||||
| @group(0) @binding(0) var<uniform> _Global: GlobalUniforms; | ||||
| @group(1) @binding(0) var<uniform> _Material: MaterialUniforms; | ||||
| @group(2) @binding(0) var<uniform> _Object: ObjectUniforms; | ||||
|  | ||||
| @group(0) @binding(1) var<storage> _PointLights: array<PointLight>; | ||||
| @group(0) @binding(2) var<storage> _DirectionalLights: array<DirectionalLight>; | ||||
|  | ||||
| @group(1) @binding(1) var _Sampler: sampler; | ||||
| @group(1) @binding(2) var _BaseColorPartialCoverageTexture: texture_2d<f32>; | ||||
| @group(1) @binding(3) var _OcclusionTexture: texture_2d<f32>; | ||||
| @group(1) @binding(4) var _RoughnessMetallicTexture: texture_2d<f32>; | ||||
| @group(1) @binding(5) var _NormalTexture: texture_2d<f32>; | ||||
| @group(1) @binding(6) var _EmissiveTexture: texture_2d<f32>; | ||||
| @group(1) @binding(7) var _TransmissionCollimationTexture: texture_2d<f32>; | ||||
|  | ||||
| @vertex | ||||
| fn vert(vertex: Vertex) -> Varyings { | ||||
| 	var output: Varyings; | ||||
| 	let positionWS = (_Object.matrixOStoWS * vec4(vertex.positionOS, 1.0)).xyz; | ||||
| 	let positionVS = (_Global.matrixWStoVS * vec4(positionWS, 1.0)).xyz; | ||||
| 	let positionCS = _Global.matrixVStoCS * vec4(positionVS, 1.0); | ||||
| 	output.positionCS = positionCS; | ||||
| 	output.positionVS = positionVS; | ||||
| 	${normal ? ` | ||||
| 		let normalWS = normalize((_Object.matrixOStoWSNormal * vec4(vertex.normalOS, 0.0)).xyz); | ||||
| 		let normalVS = normalize((_Global.matrixWStoVS * vec4(normalWS, 0.0)).xyz); | ||||
| 		output.normalVS = normalVS; | ||||
| 		${tangent ? ` | ||||
| 			let tangentWS = normalize((_Object.matrixOStoWS * vec4(vertex.tangentOS.xyz, 0.0)).xyz); | ||||
| 			let tangentVS = normalize((_Global.matrixWStoVS * vec4(tangentWS, 0.0)).xyz); | ||||
| 			let bitangentVS = vertex.tangentOS.w * normalize(cross(normalVS, tangentVS)); | ||||
| 			output.tangentVS = tangentVS; | ||||
| 			output.bitangentVS = bitangentVS; | ||||
| 		` : ""} | ||||
| 	` : ""} | ||||
| 	${texCoord ? "output.texCoord = vertex.texCoord;" : ""} | ||||
| 	${lightTexCoord ? "output.lightTexCoord = vertex.lightTexCoord;" : ""} | ||||
| } | ||||
|  | ||||
| @fragment | ||||
| fn frag(fragment: Varyings) -> @location(0) vec2<f32> { | ||||
| 	var baseColor = _Material.baseColor; | ||||
| 	var partialCoverage = _Material.partialCoverage; | ||||
| 	var occlusion = 1.0; | ||||
| 	var roughness = _Material.roughness; | ||||
| 	var metallic = _Material.metallic; | ||||
| 	var normalScale = _Material.normalScale; | ||||
| 	var emissive = _Material.emissive; | ||||
| 	var ior = _Material.ior; | ||||
| 	${texCoord ? ` | ||||
| 		let baseColorPartialCoverageTexel = texture(_BaseColorPartialCoverageTexture, _Sampler, fragment.texCoord); | ||||
| 		baseColor *= baseColorPartialCoverageTexel.rgb; | ||||
| 		partialCoverage *= baseColorPartialCoverageTexel.a; | ||||
| 		let roughnessMetallicTexel = texture(_RoughnessMetallic, _Sampler, fragment.texCoord); | ||||
| 		roughness *= roughnessMetallicTexel.g; | ||||
| 		metallic *= roughnessMetallicTexel.b; | ||||
| 		let emissiveTexel = texture(_EmissiveTexture, _Sampler, fragment.texCoord); | ||||
| 		emissive *= emissiveTexel.rgb; | ||||
| 	` : ""} | ||||
| 	${lightTexCoord ? ` | ||||
| 		let occlusionTexel = texture(_OcclusionTexture, _Sampler, fragment.lightTexCoord); | ||||
| 		occlusion += _Material.occlusionTextureStrength * (occlusionTexel.r - 1.0); | ||||
| 	` : ""} | ||||
| }`; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user