Skip to content

@blooper.gg/toodle


@blooper.gg/toodle / shaders/wgsl/quad.wgsl / default

Variable: default

default: "\nstruct InstanceData {\n // location 0-2 are the model matrix for this instanced quad\n @location(0) model0: vec4<f32>,\n @location(1) model1: vec4<f32>,\n @location(2) model2: vec4<f32>,\n // location 3 is the tint - the color will be multiplied by the texture color to determine the pixel color\n @location(3) engine_tint: vec4<f32>,\n // location 4 are the uv offset and scale used to sample the texture atlas. these are in normalized texel coordinates.\n @location(4) uvOffsetAndScale: vec4<f32>,\n // location 5 is the crop offset from center and scale. These are ratios applied to the unit quad.\n @location(5) cropOffsetAndScale: vec4<f32>,\n // location 6 is the atlas index\n @location(6) atlasIndex: u32,\n // @INSTANCE_DATA SNIPPET\n}\n\nstruct VertexInput {\n @builtin(vertex_index) VertexIndex: u32,\n @builtin(instance_index) InstanceIndex: u32,\n instance: InstanceData\n}\n\nstruct VertexOutput {\n @builtin(position) engine_clip_position : vec4<f32>,\n // uv coordinates are stored as two vec2s:\n // [0,1] = atlas uv coords\n // [2,3] = uv scale\n @location(0) engine_uv: vec4<f32>,\n @location(1) @interpolate(flat) engine_tint: vec4<f32>,\n @location(2) @interpolate(flat) engine_atlasIndex: u32,\n // @VERTEX_OUTPUT SNIPPET\n}\n\nstruct EngineUniform {\n viewProjection: mat3x3<f32>,\n resolution: vec2f,\n};\n\n// we can't divide by 2 in the projection matrix because\n// it will affect the positioning as well as the geometry scale\n// so we need to divide by 2 for the initial position scale.\n// for eg a 10x10 quad in the top left of a 100x100 logical canvas with a 200x200 natural size\n// will be passed in as\n// position=[-45, 45]\n// scale=[10,10]\n// so the top left corner will be: (-0.5 * 10 - 45) * 2 / 100 = -1\n// if the top left vertex was -1, it would be: (-1 * 10 - 45) * 2 / 100 = -1.1\nconst enginePosLookup = array(vec2f(-0.5, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, 0.5), vec2f(0.5, -0.5));\nconst engineUvLookup = array(vec2f(0, 0), vec2f(0, 1), vec2f(1, 0), vec2f(1, 1));\n\n@group(0) @binding(0) var<uniform> engineUniform: EngineUniform;\n@group(0) @binding(1) var linearSampler: sampler;\n@group(0) @binding(2) var nearestSampler: sampler;\n\n@group(1) @binding(0) var textureArray: texture_2d_array<f32>;\n\n@vertex\nfn engine_vs(\n @builtin(vertex_index) VertexIndex: u32,\n @builtin(instance_index) InstanceIndex: u32,\n instance: InstanceData\n) -> VertexOutput {\n var output = default_vertex_shader(VertexIndex, InstanceIndex,instance);\n return output;\n}\n\n@fragment\nfn engine_fs(vertex: VertexOutput) -> @location(0) vec4<f32> {\n return default_fragment_shader(vertex, nearestSampler);\n}\n\nfn default_vertex_shader(\n VertexIndex: u32,\n InstanceIndex: u32,\n instance: InstanceData\n) -> VertexOutput {\n var output : VertexOutput;\n output.engine_tint = instance.engine_tint;\n\n // reconstruct the model matrix from the instance data\n // bc we can't pass a mat3x3 as instance data\n let modelMatrix = mat3x3(instance.model0.xyz, instance.model1.xyz, instance.model2.xyz);\n\n // transform the vertex position\n let localPosition = enginePosLookup[VertexIndex];\n let cropOffset = instance.cropOffsetAndScale.xy;\n let cropScale = instance.cropOffsetAndScale.zw;\n let croppedPosition = localPosition * cropScale + cropOffset;\n let worldPosition = modelMatrix * vec3f(croppedPosition, 1.0);\n let clipPosition = engineUniform.viewProjection * worldPosition;\n output.engine_clip_position = vec4f(clipPosition, 1.0);\n\n // set the uv coordinates in the texture atlas.\n let original_uv = engineUvLookup[VertexIndex];\n // uvOffsetAndScale is a vec4 with the following values:\n // [0,1] = uv offset\n // [2,3] = uv scale\n let atlas_uv = original_uv * instance.uvOffsetAndScale.zw * cropScale + instance.uvOffsetAndScale.xy;\n // we also pack the original uv coordinates in the w and z components\n // since these can be useful in the fragment shader\n output.engine_uv = vec4f(atlas_uv, original_uv);\n output.engine_atlasIndex = u32(instance.atlasIndex);\n // @PASSTHROUGH_SNIPPET\n\n return output;\n}\n\nfn default_fragment_shader(vertex: VertexOutput, samp: sampler) -> vec4<f32> {\n let atlas_uv = vertex.engine_uv.xy;\n let original_uv = vertex.engine_uv.zw;\n\n // Force both samplers to be referenced without assignment\n // This prevents WGSLReflect from optimizing them away\n var nope: bool = false;\n if (nope) {\n _ = linearSampler;\n _ = nearestSampler;\n }\n\n let color = textureSample(textureArray, samp, atlas_uv, vertex.engine_atlasIndex);\n\n if (vertex.engine_atlasIndex == 1000u) {\n // rectangle - return a solid color\n return vec4f(1,1,1,1) * vertex.engine_tint;\n } else if (vertex.engine_atlasIndex == 1001u) {\n // circle:\n // edge width is 4 logical pixels\n let edgeWidth = 4. / max(engineUniform.resolution.x, engineUniform.resolution.y);\n // distance from center of the quad ranging from [0,1]\n let centerDistance = 2 * distance(vec2f(0.5, 0.5), original_uv);\n // alpha is 1 before edgeWidth and 0 after edgeWidth\n let alpha = 1. - smoothstep(1. - edgeWidth, 1. + edgeWidth, centerDistance);\n return vec4f(vertex.engine_tint.rgb, alpha * vertex.engine_tint.a);\n } else {\n // texture:\n return color * vertex.engine_tint;\n }\n}\n"

Defined in: shaders/wgsl/quad.wgsl.ts:1