Shader graph: alpha input on Output, Texture alpha node (#70)
game-maker / suite (push) Successful in 7m55s
game-maker / suite (push) Successful in 7m55s
Output gains a number input `alpha`; unconnected it reads the texel's alpha, like `color` reads the texel. The epilogue premultiplies with the resolved alpha. New `Texture alpha` node exposes texel.a for maths. PortDef.inline=false keeps the alpha port without an inline field: every number port is inline by default, and a typed default of 0 would have made every shaded sprite invisible. The compiler's texel fallback is typed: colour ports get rgb, number ports get texel.a. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
b0c4b4ec5a
commit
3da0ef1be2
6 files changed
+125
-12
No files matched your search
@@ -14,6 +14,8 @@ export interface PortDef {
|
||||
default?: PropertyValue;
|
||||
/** The property this port carries; its typed-in value is edited as that property (color, enum, …). */
|
||||
field?: PropertyDef;
|
||||
/** `false`: nothing to type in even for a number – unconnected, the node's own rule applies. */
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,6 +147,7 @@ export function inlineInputs(def: NodeDef): PortDef[] {
|
||||
}
|
||||
|
||||
function isInlinePort(port: PortDef): boolean {
|
||||
if (port.inline === false) return false;
|
||||
if (port.type === 'vec3') return port.field !== undefined;
|
||||
return port.type !== 'exec' && port.type !== 'object' && port.type !== 'struct';
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
floatLiteral,
|
||||
isShaderNodeDef,
|
||||
SHADER_OUTPUT,
|
||||
TEXEL_ALPHA,
|
||||
TEXEL_COLOR,
|
||||
uniformName,
|
||||
vec3Literal,
|
||||
@@ -89,7 +90,10 @@ export function compileShader(graph: Graph): ShaderBuild {
|
||||
return outputs;
|
||||
};
|
||||
|
||||
/** What feeds an input: the edge's source, the typed-in value, or – with nothing to type – the texel. */
|
||||
/**
|
||||
* What feeds an input: the edge's source, the typed-in value, or – with nothing to type – the
|
||||
* texel: its colour for a colour port, its alpha for a number port.
|
||||
*/
|
||||
const input = (node: GraphNode, port: PortDef): Expr | null => {
|
||||
const edge = edgesInto(graph, node.id, port.key)[0];
|
||||
if (edge) {
|
||||
@@ -103,7 +107,7 @@ export function compileShader(graph: Graph): ShaderBuild {
|
||||
if (port.field?.kind === 'color')
|
||||
return { code: vec3Literal(colorOf(typed ?? 0xffffff, 0xffffff)), type: 'vec3' };
|
||||
if (port.field) return { code: floatLiteral(Number(typed ?? 0)), type: 'float' };
|
||||
return TEXEL_COLOR;
|
||||
return port.type === 'number' ? TEXEL_ALPHA : TEXEL_COLOR;
|
||||
};
|
||||
|
||||
const conform = (node: GraphNode, port: PortDef, expr: Expr): Expr | null => {
|
||||
@@ -119,9 +123,10 @@ export function compileShader(graph: Graph): ShaderBuild {
|
||||
if (!output) return { compiled: null, problems, reason: 'No Output node' };
|
||||
const outputDef = nodeDef(SHADER_OUTPUT)!;
|
||||
const color = input(output, outputDef.inputs[0]!);
|
||||
const alpha = input(output, outputDef.inputs[1]!);
|
||||
|
||||
const first = graph.nodes.find((n) => n.id in problems);
|
||||
if (first || !color)
|
||||
if (first || !color || !alpha)
|
||||
return { compiled: null, problems, reason: first ? problems[first.id]! : 'Cannot compile' };
|
||||
|
||||
const uniforms = shaderParamDefs(graph).map((row) => ({ name: row.key, default: row.default }));
|
||||
@@ -135,7 +140,8 @@ export function compileShader(graph: Graph): ShaderBuild {
|
||||
' vec4 texel = texture(uTexture, vTextureCoord);',
|
||||
` vec3 ${TEXEL_COLOR.code} = texel.a > 0.0 ? texel.rgb / texel.a : texel.rgb;`,
|
||||
...lines.map((line) => ` ${line}`),
|
||||
` finalColor = vec4(clamp(${color.code}, 0.0, 1.0) * texel.a, texel.a);`,
|
||||
` float alpha = clamp(${alpha.code}, 0.0, 1.0);`,
|
||||
` finalColor = vec4(clamp(${color.code}, 0.0, 1.0) * alpha, alpha);`,
|
||||
'}',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
@@ -33,11 +33,13 @@ export function isShaderNodeDef(def: NodeDef): def is ShaderNodeDef {
|
||||
}
|
||||
|
||||
export const SHADER_TEXTURE_COLOR = 'ShaderTextureColor';
|
||||
export const SHADER_TEXTURE_ALPHA = 'ShaderTextureAlpha';
|
||||
export const SHADER_OUTPUT = 'ShaderOutput';
|
||||
export const SHADER_COLOR = 'ShaderColor';
|
||||
|
||||
/** What the fragment prelude binds: the texel's straight (unpremultiplied) colour. */
|
||||
export const TEXEL_COLOR: Expr = { code: 'rgb', type: 'vec3' };
|
||||
export const TEXEL_ALPHA: Expr = { code: 'texel.a', type: 'float' };
|
||||
|
||||
const NUMBER_STEP = 0.05;
|
||||
|
||||
@@ -98,13 +100,24 @@ export const ShaderTextureColor: ShaderNodeDef = {
|
||||
emit: () => ({ color: TEXEL_COLOR }),
|
||||
};
|
||||
|
||||
/** The one node the compiler starts from; an unconnected color leaves the pixel as it is. */
|
||||
export const ShaderTextureAlpha: ShaderNodeDef = {
|
||||
type: SHADER_TEXTURE_ALPHA,
|
||||
label: 'Texture alpha',
|
||||
category: 'shader',
|
||||
description: "The sprite's own pixel's opacity: 0 transparent, 1 opaque.",
|
||||
inputs: [],
|
||||
outputs: [{ key: 'value', label: 'value', type: 'number' }],
|
||||
params: [],
|
||||
emit: () => ({ value: TEXEL_ALPHA }),
|
||||
};
|
||||
|
||||
/** The one node the compiler starts from; an unconnected input leaves that part of the pixel as it is. */
|
||||
export const ShaderOutput: ShaderNodeDef = {
|
||||
type: SHADER_OUTPUT,
|
||||
label: 'Output',
|
||||
category: 'shader',
|
||||
description: 'The colour the sprite is drawn with. Its alpha stays the texture’s.',
|
||||
inputs: [vec3Port('color')],
|
||||
description: 'The colour and opacity the sprite is drawn with; unconnected, each is the texture’s.',
|
||||
inputs: [vec3Port('color'), { key: 'alpha', label: 'alpha', type: 'number', inline: false }],
|
||||
outputs: [],
|
||||
params: [],
|
||||
emit: () => ({}),
|
||||
@@ -194,6 +207,7 @@ export const ShaderLuminance: ShaderNodeDef = {
|
||||
/** Every node a shader graph offers. Registered by `graph/nodes`, offered by no behavior palette. */
|
||||
export const SHADER_NODES: readonly ShaderNodeDef[] = [
|
||||
ShaderTextureColor,
|
||||
ShaderTextureAlpha,
|
||||
ShaderOutput,
|
||||
ShaderProperty,
|
||||
ShaderColor,
|
||||
|
||||
Reference in new issue
Block a user