Skip to content

Commit

Permalink
🔧 fix: color space
Browse files Browse the repository at this point in the history
  • Loading branch information
deepkolos committed Oct 15, 2023
1 parent 38a74bf commit e4e46fa
Show file tree
Hide file tree
Showing 29 changed files with 1,619 additions and 181 deletions.
1,349 changes: 1,349 additions & 0 deletions example/dev/devSRGB.json

Large diffs are not rendered by default.

Binary file added example/dev/gLTF/Default_AO.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/dev/gLTF/Default_albedo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/dev/gLTF/Default_emissive.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/dev/gLTF/Default_metalRoughness.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/dev/gLTF/Default_normal.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions example/dev/gLTF/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Default_albedo from './Default_albedo.jpg';
import Default_AO from './Default_AO.jpg';
import Default_emissive from './Default_emissive.jpg';
import Default_normal from './Default_normal.jpg';
import Default_metalRoughness from './Default_metalRoughness.jpg';

export { Default_albedo, Default_AO, Default_emissive, Default_normal, Default_metalRoughness };
30 changes: 3 additions & 27 deletions example/graph.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { useState, useEffect, useRef, MutableRefObject } from 'react';
import AreaPlugin from 'rete-area-plugin';
import {
ShaderGraphEditor,
AssetSimplePlugin,
setResourceAdapter,
PreviewCustomMeshPlugin,
} from '../src';
import { ShaderGraphEditor, AssetSimplePlugin, setResourceAdapter, PreviewCustomMeshPlugin } from '../src';
import { Presets } from './presets';
import { printCompile } from '../src/view/utils';

setResourceAdapter(asset => asset?.id);

Expand Down Expand Up @@ -42,10 +38,7 @@ export async function createEditor(container: HTMLElement) {
return editor;
}

export function useRete(): [
ReturnType<typeof useState<HTMLElement>>['1'],
MutableRefObject<ShaderGraphEditor | undefined>,
] {
export function useRete(): [ReturnType<typeof useState<HTMLElement>>['1'], MutableRefObject<ShaderGraphEditor | undefined>] {
const [container, setContainer] = useState<HTMLElement>();
const editorRef = useRef<ShaderGraphEditor>();

Expand All @@ -63,20 +56,3 @@ export function useRete(): [

return [setContainer, editorRef];
}

export async function printCompile(editor?: ShaderGraphEditor) {
if (editor) {
let vertCode = '';
let fragCode = '';
if (editor.editing === 'ShaderGraph') {
({ vertCode, fragCode } = await editor.compiler.compile(editor.toJSON()));
}
if (editor.editing === 'SubGraph') {
({ vertCode, fragCode } = await editor.compiler.compileSubGraphPreview(editor.toJSON()));
}
console.log('===== vertCode =====');
console.log('%c' + vertCode, 'font-size: 14px');
console.log('===== fragCode =====');
console.log('%c' + fragCode, 'font-size: 14px');
}
}
3 changes: 2 additions & 1 deletion example/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import './index.css';
import { createRoot } from 'react-dom/client';
import React, { FC, MutableRefObject, useEffect, useState } from 'react';
import { printCompile, useRete } from './graph';
import { useRete } from './graph';
import copy from 'copy-to-clipboard';
import { Select, ShaderGraphEditor } from '../src';
import { Presets } from './presets';
import { printCompile } from '../src/view/utils';

let toasted = false;
const GraphKey = 'graph';
Expand Down
18 changes: 18 additions & 0 deletions example/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import devSubGraphUsage from './dev/devSubGraphUsage.json';
import devArtistic from './dev/devArtistic.json';
import devUV from './dev/devUV.json';
import devInput from './dev/devInput.json';
import devSRGB_ from './dev/devSRGB.json';
import demoGradient from './demo/demoGradient.json';
import demoDissolve from './demo/demoDissolve.json';
import demoFresnelOutline from './demo/demoFresnelOutline.json';
Expand All @@ -33,6 +34,22 @@ import demoImageFlip from './demo/demoImageFlip.json';
import demoCartoonWater from './demo/demoCartoonWater';
import demoSkybox from './demo/demoSkybox.json';

import * as gLTF from './dev/gLTF';

const replaceGLTFTex = <T>(json: T): T => {
const data = JSON.parse(JSON.stringify(json)) as any;
const gLTFKeys = Object.keys(gLTF);
Object.values(data.nodes).forEach((node: any) => {
if (node.name === 'SampleTexture2D' && node.data.textureValue) {
const { label } = node.data.textureValue;
// @ts-ignore
node.data.textureValue.id = gLTF[gLTFKeys.find(i => label.includes(i))];
}
});
return data as T;
};
const devSRGB = replaceGLTFTex(devSRGB_);

export const Presets = {
devCompile,
devVarying,
Expand All @@ -59,6 +76,7 @@ export const Presets = {
devArtistic,
devUV,
devInput,
devSRGB,
demoGradient,
demoDissolve,
demoFresnelOutline,
Expand Down
136 changes: 52 additions & 84 deletions src/compilers/ShaderGraphCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
OutputRC,
ReteParameterNode,
TransformationMatrixRC,
ColorSpaceConversionRC,
} from '../components';
import { RC } from '../components/ReteComponent';
import { ShaderGraphData, SGNodeData, SGNodes, SubGraphProvider } from '../editors';
Expand All @@ -24,8 +25,9 @@ import {
isMatrixType,
VectorValueType,
SamplerValue,
ValueUsage,
} from '../types';
import { hash, lowerCaseFirstLetter, removeWhiteSpace } from '../utils';
import { SRGBToLinear, hash, lowerCaseFirstLetter, removeWhiteSpace } from '../utils';
import { GraphCompiler } from './GraphCompiler';
import {
Context,
Expand Down Expand Up @@ -85,12 +87,17 @@ export class ShaderGraphCompiler extends GraphCompiler {
if (node.data.outValueType === ValueType.texture2d) {
return this.compileValue(node.data.outValue, node.data.outValueType);
}
return this.setContext(
'uniforms',
node,
node.data.outValueName,
varName => `${varName}: ${this.getTypeClass(node.data.outValueType)}`,
);
const uniformVar = this.setContext('uniforms', node, node.data.outValueName, varName => `${varName}: ${this.getTypeClass(node.data.outValueType)}`);
if (node.data.outValueUsage === ValueUsage.Color) {
// 目前实际输入的是sRGB, 后面再同步接入最新three的颜色空间管理
const SRGBToLinear = ColorSpaceConversionRC.initFnContext(this, 'sRGB', 'Linear');
const codeFn = (varName: string) => /* wgsl */ `let ${varName} = ${SRGBToLinear}(${uniformVar});`;
const fragVar = this.setContext('fragShared', node, node.data.outValueName, codeFn);
const vertVar = this.setContext('vertShared', node, node.data.outValueName, codeFn);
return this.setVarNameMap(node, node.data.outValueName, vertVar, fragVar);
}

return uniformVar
}

setVarNameMap(node: NodeName, key: string, vertName: string, fragName: string, varName?: string) {
Expand Down Expand Up @@ -119,12 +126,7 @@ export class ShaderGraphCompiler extends GraphCompiler {

setContext(type: ContextKeys, node: NodeName, key: string, item: ContextItem): string;
setContext(type: ContextKeys, node: NodeName, key: string, codeFn: CodeFn): string;
setContext(
type: ContextKeys,
node: NodeName,
key: string,
itemOrCode: ContextItem | CodeFn,
): string {
setContext(type: ContextKeys, node: NodeName, key: string, itemOrCode: ContextItem | CodeFn): string {
const contextKey = this.getContextKey(node, key);

if (!this.context[type as ContextKeys][contextKey]) {
Expand Down Expand Up @@ -177,11 +179,7 @@ export class ShaderGraphCompiler extends GraphCompiler {
}

/** 读取分量 只支持vec1234 */
getVarChannel(
varName: string,
inType: ValueType,
channel: 'r' | 'g' | 'b' | 'a' | 'x' | 'y' | 'z' | 'w',
) {
getVarChannel(varName: string, inType: ValueType, channel: 'r' | 'g' | 'b' | 'a' | 'x' | 'y' | 'z' | 'w') {
const len = ValueComponentMap[inType];
const channelLenNeeds = ChannelLenNeedsMap[channel];
if (channelLenNeeds > len) return '0.0';
Expand All @@ -208,10 +206,7 @@ export class ShaderGraphCompiler extends GraphCompiler {
if (inType === ValueType.float) return `${this.getTypeClass(outType)}(${varName})`;
// 升 vec2 > vec3 vec3 > vec4
// vec2 > vec4
if (
(inType === ValueType.vec2 && outType === ValueType.vec3) ||
(inType === ValueType.vec3 && outType === ValueType.vec4)
) {
if ((inType === ValueType.vec2 && outType === ValueType.vec3) || (inType === ValueType.vec3 && outType === ValueType.vec4)) {
return `${this.getTypeClass(outType)}(${varName}, 0)`;
} else if (inType === ValueType.vec2 && outType === ValueType.vec4) {
return `${this.getTypeClass(outType)}(${varName}, 0, 0)`;
Expand Down Expand Up @@ -240,25 +235,16 @@ export class ShaderGraphCompiler extends GraphCompiler {

getInputType(node: SGNodeData<SGNodes>, inputKey: string): ValueType {
const inCon = node.inputs[inputKey]?.connections[0];
if (inCon)
return this.graphData.nodes[inCon.node].data[inCon.output + 'ValueType'] as ValueType;
if (inCon) return this.graphData.nodes[inCon.node].data[inCon.output + 'ValueType'] as ValueType;
return node.data[inputKey + 'ValueType'];
}

getInputVarConverted(node: SGNodeData<SGNodes>, inputKey: string): string;
getInputVarConverted(
node: SGNodeData<SGNodes>,
inputKey: string,
fallback: false,
): string | undefined;
getInputVarConverted(node: SGNodeData<SGNodes>, inputKey: string, fallback: false): string | undefined;
getInputVarConverted(node: SGNodeData<SGNodes>, inputKey: string, fallback = true) {
const inType = this.getInputType(node, inputKey);
if (fallback) {
return this.typeConvert(
this.getInputVar(node, inputKey),
inType,
node.data[inputKey + 'ValueType'],
);
return this.typeConvert(this.getInputVar(node, inputKey), inType, node.data[inputKey + 'ValueType']);
} else {
const inVar = this.getInputVar(node, inputKey, false);
if (inVar) return this.typeConvert(inVar, inType, node.data[inputKey + 'ValueType']);
Expand Down Expand Up @@ -304,19 +290,19 @@ export class ShaderGraphCompiler extends GraphCompiler {
return 'sg_' + this.getContextKey(node, key);
}

compileValue(value: any, type: ValueType) {
compileValue(value: any, type: ValueType, usage?: ValueUsage) {
if (Number.isNaN(value) || (Array.isArray(value) && value.some(i => Number.isNaN(i)))) {
console.warn(`value contains NaN`, value, type);
}
switch (type) {
case ValueType.float:
return stringifyFloat(value);
case ValueType.vec2:
return `vec2<f32>(${value[0] || 0}, ${value[1] || 0})`;
return stringifyVector(value, 2);
case ValueType.vec3:
return `vec3<f32>(${value[0] || 0}, ${value[1] || 0}, ${value[2] || 0})`;
return usage === ValueUsage.Color ? stringifyVector(value.map(SRGBToLinear), 3) : stringifyVector(value, 3);
case ValueType.vec4:
return `vec4<f32>(${value[0] || 0}, ${value[1] || 0}, ${value[2] || 0}, ${value[3] || 0})`;
return usage === ValueUsage.Color ? stringifyVector(value.map(SRGBToLinear), 4) : stringifyVector(value, 4);
case ValueType.mat2:
case ValueType.mat3:
case ValueType.mat4:
Expand All @@ -326,25 +312,15 @@ export class ShaderGraphCompiler extends GraphCompiler {
if (!asset) return '';
const key = hash(asset.id);
const node = { data: {}, name: 'Texture2D' } as any;
const outVar = this.setContext(
'bindings',
node,
key,
(varName, i) => `@group(0) @binding(${i}) var ${varName}: texture_2d<f32>;`,
);
const outVar = this.setContext('bindings', node, key, (varName, i) => `@group(0) @binding(${i}) var ${varName}: texture_2d<f32>;`);
this.setResource('texture', node, key, value);
return outVar;
}
case ValueType.sampler: {
const sampler = (value || { filter: 'point', warp: 'clamp' }) as SamplerValue;
const key = sampler.filter + '_' + sampler.warp;
const node = { data: {}, name: 'Sampler' } as any;
const outVar = this.setContext(
'bindings',
node,
key,
(varName, i) => `@group(0) @binding(${i}) var ${varName}: sampler;`,
);
const outVar = this.setContext('bindings', node, key, (varName, i) => `@group(0) @binding(${i}) var ${varName}: sampler;`);
this.setResource('sampler', node, key, sampler);
return outVar;
}
Expand All @@ -354,7 +330,7 @@ export class ShaderGraphCompiler extends GraphCompiler {
}

compileNodeValue(node: SGNodeData<SGNodes>, key: string) {
return this.compileValue(node.data[key + 'Value'], node.data[key + 'ValueType']);
return this.compileValue(node.data[key + 'Value'], node.data[key + 'ValueType'], node.data[key + 'ValueUsage']);
}

compileHeadCode(body: string, scope: 'vert' | 'frag') {
Expand Down Expand Up @@ -384,9 +360,7 @@ export class ShaderGraphCompiler extends GraphCompiler {
code = [
'struct Varying {',
' @builtin(position) position: vec4<f32>,',
...items
.filter(i => testCode.includes(i.varName.replace('v.', '')))
.map((i, k) => ` @location(${k}) ${i.code},`),
...items.filter(i => testCode.includes(i.varName.replace('v.', ''))).map((i, k) => ` @location(${k}) ${i.code},`),
'};',
].join('\n');
} else {
Expand All @@ -404,17 +378,12 @@ ${code}`;
return headCode ? headCode + '\n\n' : '';
}

getLinkedVaryingNodes(
nodeId: number,
output: Array<SGNodeData<ReteVaryingNode>> = [],
): Array<SGNodeData<ReteVaryingNode>> {
getLinkedVaryingNodes(nodeId: number, output: Array<SGNodeData<ReteVaryingNode>> = []): Array<SGNodeData<ReteVaryingNode>> {
const nodeData = this.graphData.nodes[nodeId];
if (!nodeData) return [];
if (nodeData.name === VaryingRC.Name) output.push(nodeData as SGNodeData<ReteVaryingNode>);

Object.values(nodeData.inputs).forEach(i =>
i.connections.forEach(con => this.getLinkedVaryingNodes(con.node, output)),
);
Object.values(nodeData.inputs).forEach(i => i.connections.forEach(con => this.getLinkedVaryingNodes(con.node, output)));
return output;
}

Expand Down Expand Up @@ -449,10 +418,7 @@ ${code}`;

doVarMap(body: string, scope: 'vert' | 'frag') {
return Object.values(this.varNameMap).reduce((body, map) => {
return body.replace(
new RegExp(`(${map.varName})`, 'g'),
scope === 'frag' ? map.fragName : map.vertName,
);
return body.replace(new RegExp(`(${map.varName})`, 'g'), scope === 'frag' ? map.fragName : map.vertName);
}, body);
}

Expand Down Expand Up @@ -516,31 +482,21 @@ ${code}`;
// if (!vert) throw new Error('missing Vertex Context');
const varyingNodes = this.getLinkedVaryingNodes(node.id);
const varyingBlocks = (vert?.blocks || []).filter(
i =>
i.name === CustomInterpolatorBlock.Name &&
varyingNodes.some(node => node.data.outValueName === i.data.varyingValueName),
i => i.name === CustomInterpolatorBlock.Name && varyingNodes.some(node => node.data.outValueName === i.data.varyingValueName),
);
let vertBody = this.linkBlocks(varyingBlocks);
let fragCode = '';
let vertCode = '';

if (node.name === VaryingRC.Name) {
if (!varyingBlocks[0])
throw new Error('compile varying preview failed: missing CustomInterpolatorBlock');
const { varName } = this.getContext(
'varyings',
varyingBlocks[0],
varyingBlocks[0].data.varyingValueName,
)!;
const bodyCode = SGTemplates.unlit.frag(
this.prependFragSharedCode(`*baseColor = ${varName}.xyz;`),
);
if (!varyingBlocks[0]) throw new Error('compile varying preview failed: missing CustomInterpolatorBlock');
const { varName } = this.getContext('varyings', varyingBlocks[0], varyingBlocks[0].data.varyingValueName)!;
const bodyCode = SGTemplates.unlit.frag(this.prependFragSharedCode(`*baseColor = ${varName}.xyz;`));
const headCode = this.compileHeadCode(bodyCode, 'frag');
fragCode = headCode + '\n' + bodyCode;
vertBody += this.getAutoVaryingsCode(fragCode);
vertBody = this.prependVertSharedCode(vertBody);
vertCode =
SG_VERT + this.compileHeadCode(vertBody, 'vert') + SGTemplates.unlit.vert(vertBody);
vertCode = SG_VERT + this.compileHeadCode(vertBody, 'vert') + SGTemplates.unlit.vert(vertBody);
} else {
const baseColorData = await ctx.getNodeData<ReteBaseColorBlock>(ctx.baseColorBlock);
const output = [...node.outputs.keys()][0];
Expand All @@ -557,8 +513,7 @@ ${code}`;
fragCode = this.compileHeadCode(fragBody, 'frag') + SGTemplates.unlit.frag(fragBody);
vertBody += this.getAutoVaryingsCode(fragCode);
vertBody = this.prependVertSharedCode(vertBody);
vertCode =
SG_VERT + this.compileHeadCode(vertBody, 'vert') + SGTemplates.unlit.vert(vertBody);
vertCode = SG_VERT + this.compileHeadCode(vertBody, 'vert') + SGTemplates.unlit.vert(vertBody);
}

return { ...this.compilation, fragCode, vertCode };
Expand Down Expand Up @@ -650,12 +605,25 @@ ${code}`;
}
}

const stringifyFloat = (num: number): string => {
const stringifyFloat = (num: number | number[]): string => {
if (Array.isArray(num)) {
num = num[0] || 0;
}
const str = String(num);
if (str.includes('.')) return str;
const dotIndex = str.indexOf('.');
if (dotIndex > -1) {
return str;
}
return str + '.0';
};

const stringifyVector = (value: number[], len: 2 | 3 | 4): string => {
return `vec${len}(${new Array(len)
.fill(0)
.map((v, k) => stringifyFloat(value[k] || 0))
.join(', ')})`;
};

export class SubGraphCompiler extends ShaderGraphCompiler {
constructor(public sgCompiler: ShaderGraphCompiler) {
super();
Expand Down
Loading

0 comments on commit e4e46fa

Please sign in to comment.