mirror of
https://github.com/galacean/engine.git
synced 2026-06-08 08:42:47 +08:00
Provide shaderlab compiled AST related context for the VSCode extension (#1882)
* feat(shader-lab): provide `shaderlab` compiled AST related context for the VSCode extension
This commit is contained in:
@@ -31,8 +31,8 @@ export class AstNodeUtils {
|
||||
|
||||
static defaultVisit(this: ICstVisitor<any, AstNode>, ctx: CstChildrenDictionary): ObjectAstNode {
|
||||
const content = {} as Record<string, AstNode>;
|
||||
let start: IPosition = { line: Number.MAX_SAFE_INTEGER, offset: -1 },
|
||||
end: IPosition = { line: 0, offset: -1 };
|
||||
let start: IPosition = { line: Number.MAX_SAFE_INTEGER, character: -1 },
|
||||
end: IPosition = { line: 0, character: -1 };
|
||||
|
||||
for (const k in ctx) {
|
||||
if (AstNodeUtils.isCstNode(ctx[k][0])) {
|
||||
@@ -63,11 +63,11 @@ export class AstNodeUtils {
|
||||
return {
|
||||
start: {
|
||||
line: token.startLine,
|
||||
offset: token.startColumn
|
||||
character: token.startColumn
|
||||
},
|
||||
end: {
|
||||
line: token.endLine,
|
||||
offset: token.endColumn
|
||||
character: token.endColumn
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -88,7 +88,7 @@ export class AstNodeUtils {
|
||||
|
||||
static astSortAsc(a: AstNode, b: AstNode) {
|
||||
return a.position.start.line > b.position.start.line ||
|
||||
(a.position.start.line === b.position.start.line && a.position.start.offset >= b.position.start.offset)
|
||||
(a.position.start.line === b.position.start.line && a.position.start.character >= b.position.start.character)
|
||||
? 1
|
||||
: -1;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ interface IReferenceStructInfo {
|
||||
}
|
||||
|
||||
export default class RuntimeContext {
|
||||
private _shaderAst: AstNode<IShaderAstContent>;
|
||||
|
||||
get shaderAst() {
|
||||
return this._shaderAst;
|
||||
}
|
||||
|
||||
functionAstStack: { fnAst: FnAstNode; localDeclaration: VariableDeclarationAstNode[] }[] = [];
|
||||
/** Diagnostic for linting service. */
|
||||
diagnostics: IDiagnostic[] = [];
|
||||
@@ -123,6 +129,7 @@ export default class RuntimeContext {
|
||||
}
|
||||
|
||||
parse(ast: AstNode<IShaderAstContent>): IShaderInfo {
|
||||
this._shaderAst = ast;
|
||||
this._shaderReset();
|
||||
|
||||
this._initShaderGlobalList(ast);
|
||||
@@ -144,6 +151,12 @@ export default class RuntimeContext {
|
||||
return ret;
|
||||
}
|
||||
|
||||
private _resetPassScopeGlobalReference() {
|
||||
for (const [_, g] of this._passGlobalMap) {
|
||||
g.referenced = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _parsePassProperty(
|
||||
passAst: AstNode<IPassAstContent>,
|
||||
prop: PassPropertyAssignmentAstNode,
|
||||
@@ -160,7 +173,7 @@ export default class RuntimeContext {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._initPassGlobalList(passAst);
|
||||
this._resetPassScopeGlobalReference();
|
||||
ret.vertexSource = Ast2GLSLUtils.stringifyVertexFunction(passAst, prop, this);
|
||||
break;
|
||||
case FRAG_FN_NAME:
|
||||
@@ -172,7 +185,7 @@ export default class RuntimeContext {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._initPassGlobalList(passAst);
|
||||
this._resetPassScopeGlobalReference();
|
||||
ret.fragmentSource = Ast2GLSLUtils.stringifyFragmentFunction(passAst, prop, this);
|
||||
break;
|
||||
default:
|
||||
@@ -192,12 +205,14 @@ export default class RuntimeContext {
|
||||
}
|
||||
|
||||
parsePassInfo(ast: AstNode<IPassAstContent | IUsePassAstContent>): IShaderPassInfo | string {
|
||||
this._passReset();
|
||||
|
||||
if (typeof ast.content === "string") {
|
||||
// UsePass
|
||||
return ast.content;
|
||||
}
|
||||
|
||||
this._passReset();
|
||||
this._initPassGlobalList(<AstNode<IPassAstContent>>ast);
|
||||
|
||||
const ret = {} as IShaderPassInfo;
|
||||
ret.name = ast.content.name;
|
||||
ret.tags = ast.content.tags?.getContentValue();
|
||||
@@ -206,7 +221,7 @@ export default class RuntimeContext {
|
||||
|
||||
this.payload = { parsingRenderState: true };
|
||||
const tmpRenderStates = ast.content.renderStates ?? [];
|
||||
ast.content.properties.forEach((prop) =>
|
||||
ast.content.properties?.forEach((prop) =>
|
||||
this._parsePassProperty(<AstNode<IPassAstContent>>ast, prop, ret, tmpRenderStates)
|
||||
);
|
||||
for (const rs of tmpRenderStates) {
|
||||
|
||||
@@ -4,12 +4,22 @@ import { ShaderVisitor } from "./ShaderVisitor";
|
||||
import RuntimeContext from "./RuntimeContext";
|
||||
|
||||
export class ShaderLab implements IShaderLab {
|
||||
/** @internal */
|
||||
private _parser: ShaderParser;
|
||||
/** @internal */
|
||||
private _visitor: ShaderVisitor;
|
||||
/** @internal */
|
||||
private _context: RuntimeContext;
|
||||
|
||||
/** @internal */
|
||||
get context() {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._parser = new ShaderParser();
|
||||
this._visitor = new ShaderVisitor();
|
||||
this._context = new RuntimeContext();
|
||||
}
|
||||
|
||||
parseShader(shaderSource: string) {
|
||||
@@ -26,8 +36,7 @@ export class ShaderLab implements IShaderLab {
|
||||
|
||||
const ast = this._visitor.visit(cst);
|
||||
|
||||
const context = new RuntimeContext();
|
||||
const shaderInfo = context.parse(ast);
|
||||
const shaderInfo = this._context.parse(ast);
|
||||
|
||||
return shaderInfo;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
FnBlockStatementAstNode,
|
||||
FnBodyAstNode,
|
||||
FnCallAstNode,
|
||||
FnCallStatementAstNode,
|
||||
FnConditionStatementAstNode,
|
||||
FnMacroConditionAstNode,
|
||||
FnMacroConditionElifBranchAstNode,
|
||||
@@ -96,6 +97,7 @@ import {
|
||||
_ruleFnBlockStatementCstChildren,
|
||||
_ruleFnBodyCstChildren,
|
||||
_ruleFnCallCstChildren,
|
||||
_ruleFnCallStatementCstChildren,
|
||||
_ruleFnConditionStatementCstChildren,
|
||||
_ruleFnCstChildren,
|
||||
_ruleFnExpressionCstChildren,
|
||||
@@ -300,8 +302,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
}
|
||||
|
||||
_ruleFnBody(ctx: _ruleFnBodyCstChildren) {
|
||||
let start: IPosition = { line: Number.MAX_SAFE_INTEGER, offset: -1 },
|
||||
end: IPosition = { line: 0, offset: -1 };
|
||||
let start: IPosition = { line: Number.MAX_SAFE_INTEGER, character: -1 },
|
||||
end: IPosition = { line: 0, character: -1 };
|
||||
|
||||
const iterate = (item: CstNode) => {
|
||||
const astInfo = this.visit(item);
|
||||
@@ -474,20 +476,16 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
}
|
||||
|
||||
_ruleConditionExpr(children: _ruleConditionExprCstChildren, param?: any) {
|
||||
const leftExpr = this.visit(children._ruleFnRelationExpr[0]);
|
||||
let position: IPositionRange;
|
||||
if (children._ruleRelationOperator) {
|
||||
const rightExpr = this.visit(children._ruleFnRelationExpr[1]);
|
||||
const operator = this.visit(children._ruleRelationOperator);
|
||||
const expressionList = children._ruleFnRelationExpr.map((item) => this.visit(item)).sort(AstNodeUtils.astSortAsc);
|
||||
const operatorList = children._ruleRelationOperator?.map((item) => this.visit(item)).sort(AstNodeUtils.astSortAsc);
|
||||
|
||||
position = {
|
||||
start: leftExpr.position.start,
|
||||
end: rightExpr.position.end
|
||||
};
|
||||
return new ConditionExprAstNode(position, { leftExpr, rightExpr, operator });
|
||||
const leftExpr = this.visit(children._ruleFnRelationExpr[0]);
|
||||
const position: IPositionRange = expressionList[0].position;
|
||||
if (operatorList?.length) {
|
||||
position.end = expressionList[expressionList.length - 1].position.end;
|
||||
}
|
||||
position = leftExpr.position;
|
||||
return new ConditionExprAstNode(position, { leftExpr });
|
||||
|
||||
return new ConditionExprAstNode(position, { expressionList, operatorList });
|
||||
}
|
||||
|
||||
_ruleFnRelationExpr(ctx: _ruleFnRelationExprCstChildren) {
|
||||
@@ -697,6 +695,16 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
return new FnReturnStatementAstNode(position, this.visit(ctx._ruleReturnBody));
|
||||
}
|
||||
|
||||
_ruleFnCallStatement(children: _ruleFnCallStatementCstChildren, param?: any): FnCallStatementAstNode {
|
||||
const fnCall = this.visit(children._ruleFnCall);
|
||||
const position: IPositionRange = {
|
||||
start: fnCall.position.start,
|
||||
end: AstNodeUtils.getTokenPosition(children.Semicolon[0]).end
|
||||
};
|
||||
|
||||
return new FnCallStatementAstNode(position, fnCall);
|
||||
}
|
||||
|
||||
_ruleReturnBody(children: _ruleReturnBodyCstChildren, param?: any) {
|
||||
const ret: ObjectAstNode<any> = AstNodeUtils.defaultVisit.bind(this)(children).content;
|
||||
return Object.values(ret)[0];
|
||||
@@ -741,8 +749,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
|
||||
_ruleBlendStatePropertyDeclaration(children: _ruleBlendStatePropertyDeclarationCstChildren, param?: any) {
|
||||
const position: IPositionRange = {
|
||||
start: { line: children.BlendState[0].startLine, offset: children.BlendState[0].startOffset },
|
||||
end: { line: children.RCurly[0].endLine, offset: children.RCurly[0].endOffset }
|
||||
start: { line: children.BlendState[0].startLine, character: children.BlendState[0].startOffset },
|
||||
end: { line: children.RCurly[0].endLine, character: children.RCurly[0].endOffset }
|
||||
};
|
||||
|
||||
const variable = children.Identifier?.[0].image;
|
||||
@@ -809,8 +817,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
|
||||
_ruleRasterStatePropertyDeclaration(children: _ruleRasterStatePropertyDeclarationCstChildren, param?: any) {
|
||||
const position: IPositionRange = {
|
||||
start: { line: children.RasterState[0].startLine, offset: children.RasterState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, offset: children.RCurly[0].startOffset }
|
||||
start: { line: children.RasterState[0].startLine, character: children.RasterState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, character: children.RCurly[0].startOffset }
|
||||
};
|
||||
|
||||
const variable = children.Identifier?.[0].image;
|
||||
@@ -823,8 +831,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
|
||||
_ruleDepthSatePropertyDeclaration(children: _ruleDepthSatePropertyDeclarationCstChildren, param?: any) {
|
||||
const position: IPositionRange = {
|
||||
start: { line: children.DepthState[0].startLine, offset: children.DepthState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, offset: children.RCurly[0].startOffset }
|
||||
start: { line: children.DepthState[0].startLine, character: children.DepthState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, character: children.RCurly[0].startOffset }
|
||||
};
|
||||
|
||||
const variable = children.Identifier?.[0].image;
|
||||
@@ -879,8 +887,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial<I
|
||||
|
||||
_ruleStencilStatePropertyDeclaration(children: _ruleStencilStatePropertyDeclarationCstChildren, param?: any) {
|
||||
const position: IPositionRange = {
|
||||
start: { line: children.StencilState[0].startLine, offset: children.StencilState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, offset: children.RCurly[0].startOffset }
|
||||
start: { line: children.StencilState[0].startLine, character: children.StencilState[0].startOffset },
|
||||
end: { line: children.RCurly[0].startLine, character: children.RCurly[0].startOffset }
|
||||
};
|
||||
|
||||
const variable = children.Identifier?.[0].image;
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
IFnBlockStatementAstContent,
|
||||
IFnBodyAstContent,
|
||||
IFnCallAstContent,
|
||||
IFnCallStatementAstContent,
|
||||
IFnConditionStatementAstContent,
|
||||
IFnMacroConditionAstContent,
|
||||
IFnMacroConditionElifBranchAstContent,
|
||||
@@ -76,7 +77,7 @@ import {
|
||||
|
||||
export interface IPosition {
|
||||
line: number;
|
||||
offset: number;
|
||||
character: number;
|
||||
}
|
||||
|
||||
export interface IPositionRange {
|
||||
@@ -88,8 +89,7 @@ export class AstNode<T = any> {
|
||||
position: IPositionRange;
|
||||
content: T;
|
||||
|
||||
/** @internal */
|
||||
_isAstNode = true;
|
||||
_astType = "unknown";
|
||||
|
||||
constructor(position: IPositionRange, content: T) {
|
||||
this.position = position;
|
||||
@@ -168,6 +168,7 @@ export class AstNode<T = any> {
|
||||
}
|
||||
|
||||
export class ReturnTypeAstNode extends AstNode<IFnReturnTypeAstContent> {
|
||||
override _astType = "ReturnType";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
return this.content.text;
|
||||
}
|
||||
@@ -177,13 +178,14 @@ export class ObjectAstNode<T = any> extends AstNode<Record<string, AstNode<T>>>
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
const astList = Object.values(this.content)
|
||||
.sort(AstNodeUtils.astSortAsc)
|
||||
.filter((item) => item._isAstNode);
|
||||
.filter((item) => item._astType);
|
||||
|
||||
return astList.map((ast) => ast.serialize(context)).join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
export class FnAstNode extends AstNode<IFnAstContent> {
|
||||
override _astType: string = "Function";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
context.functionAstStack.push({ fnAst: this, localDeclaration: [] });
|
||||
|
||||
@@ -220,6 +222,7 @@ export class FnAstNode extends AstNode<IFnAstContent> {
|
||||
}
|
||||
|
||||
export class FnBodyAstNode extends AstNode<IFnBodyAstContent> {
|
||||
override _astType: string = "FunctionBody";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
const statements = [...(this.content.macros ?? []), ...(this.content.statements ?? [])].sort(
|
||||
(a, b) => a.position.start.line - b.position.start.line
|
||||
@@ -229,6 +232,7 @@ export class FnBodyAstNode extends AstNode<IFnBodyAstContent> {
|
||||
}
|
||||
|
||||
export class FnMacroDefineAstNode extends AstNode<IFnMacroDefineAstContent> {
|
||||
override _astType: string = "MacroDefine";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
if (context?.currentMainFnAst) context.referenceGlobal(this.content.variable.getVariableName());
|
||||
return `#define ${this.content.variable.serialize(context)} ${this.content.value?.serialize(context) ?? ""}`;
|
||||
@@ -248,6 +252,7 @@ export class FnMacroDefineVariableAstNode extends AstNode<IFnMacroDefineVariable
|
||||
}
|
||||
|
||||
export class FnMacroUndefineAstNode extends AstNode<IFnMacroUndefineAstContent> {
|
||||
override _astType: string = "MacroUndef";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return `#undef ${this.content.variable}`;
|
||||
}
|
||||
@@ -277,18 +282,21 @@ export class FnMacroConditionElseBranchAstNode extends AstNode<IFnMacroCondition
|
||||
}
|
||||
|
||||
export class DiscardStatementAstNode extends AstNode {
|
||||
override _astType: string = "Discard";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return "discard;";
|
||||
}
|
||||
}
|
||||
|
||||
export class BreakStatementAstNode extends AstNode {
|
||||
override _astType: string = "Break";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return "break;";
|
||||
}
|
||||
}
|
||||
|
||||
export class ContinueStatementAstNode extends AstNode {
|
||||
override _astType: string = "Continue";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return "continue;";
|
||||
}
|
||||
@@ -305,6 +313,7 @@ export class FnParenthesisAtomicAstNode extends AstNode<IParenthesisAtomicAstCon
|
||||
}
|
||||
|
||||
export class FnCallAstNode extends AstNode<IFnCallAstContent> {
|
||||
override _astType: string = "FunctionCall";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
if (this.content.isCustom) {
|
||||
if (!context.referenceGlobal(this.content.function)) {
|
||||
@@ -315,7 +324,7 @@ export class FnCallAstNode extends AstNode<IFnCallAstContent> {
|
||||
});
|
||||
}
|
||||
}
|
||||
const args = this.content.args.map((item) => item.serialize(context)).join(", ");
|
||||
const args = this.content.args?.map((item) => item.serialize(context)).join(", ");
|
||||
return `${this.content.function}(${args})`;
|
||||
}
|
||||
|
||||
@@ -365,10 +374,13 @@ export class RelationOperatorAstNode extends AstNode<IRelationOperatorAstContent
|
||||
|
||||
export class ConditionExprAstNode extends AstNode<IConditionExprAstContent> {
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
let ret = this.content.leftExpr.serialize(context);
|
||||
if (this.content.operator) {
|
||||
ret += ` ${this.content.operator?.serialize(context)} ${this.content.rightExpr.serialize(context)}`;
|
||||
const expressionList = this.content.expressionList.map((item) => item.serialize(context));
|
||||
const operatorList = this.content.operatorList?.map((item) => item.serialize(context));
|
||||
let ret = expressionList[0];
|
||||
for (let i = 1; i < expressionList.length; i++) {
|
||||
ret += ` ${operatorList[i - 1]} ${expressionList[i]}`;
|
||||
}
|
||||
|
||||
return `${ret}`;
|
||||
}
|
||||
}
|
||||
@@ -439,6 +451,7 @@ export class FnAtomicExprAstNode extends AstNode<IFnAtomicExprAstContent> {
|
||||
}
|
||||
|
||||
export class NumberAstNode extends AstNode<INumberAstContent> {
|
||||
override _astType: string = "Number";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
return this.content.text;
|
||||
}
|
||||
@@ -449,6 +462,7 @@ export class NumberAstNode extends AstNode<INumberAstContent> {
|
||||
}
|
||||
|
||||
export class BooleanAstNode extends AstNode<IBooleanAstContent> {
|
||||
override _astType: string = "Boolean";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
return this.content.text;
|
||||
}
|
||||
@@ -459,6 +473,7 @@ export class BooleanAstNode extends AstNode<IBooleanAstContent> {
|
||||
}
|
||||
|
||||
export class FnVariableAstNode extends AstNode<IFnVariableAstContent> {
|
||||
override _astType: string = "Variable";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
const objName = this.content.variable;
|
||||
const propName = this.content.properties?.[0].content;
|
||||
@@ -498,6 +513,7 @@ export class FnVariableAstNode extends AstNode<IFnVariableAstContent> {
|
||||
}
|
||||
|
||||
export class FnArrayVariableAstNode extends AstNode<IFnArrayVariableAstContent> {
|
||||
override _astType: string = "ArrayVariable";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return this.content.variable;
|
||||
}
|
||||
@@ -513,7 +529,14 @@ export class FnReturnStatementAstNode extends AstNode<IFnReturnStatementAstConte
|
||||
}
|
||||
}
|
||||
|
||||
export class FnCallStatementAstNode extends AstNode<IFnCallStatementAstContent> {
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return `${this.content.serialize(context)};`;
|
||||
}
|
||||
}
|
||||
|
||||
export class FnArgAstNode extends AstNode<IFnArgAstContent> {
|
||||
override _astType: string = "FunctionArgument";
|
||||
override _doSerialization(context: RuntimeContext, args?: any): string {
|
||||
context.currentFunctionInfo.localDeclaration.push(
|
||||
new VariableDeclarationAstNode(this.position, {
|
||||
@@ -530,6 +553,7 @@ export class FnArgAstNode extends AstNode<IFnArgAstContent> {
|
||||
}
|
||||
|
||||
export class RenderStateDeclarationAstNode extends AstNode<IRenderStateDeclarationAstContent> {
|
||||
override _astType: string = "RenderState";
|
||||
override getContentValue(context?: RuntimeContext): {
|
||||
variable: string;
|
||||
properties: IShaderPassInfo["renderStates"];
|
||||
@@ -601,12 +625,14 @@ export class AssignableValueAstNode extends AstNode<IAssignableValueAstContent>
|
||||
}
|
||||
}
|
||||
export class VariableTypeAstNode extends AstNode<IVariableTypeAstContent> {
|
||||
override _astType: string = "VariableType";
|
||||
override _doSerialization(context: RuntimeContext): string {
|
||||
return this.content.text;
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableDeclarationAstNode extends AstNode<IFnVariableDeclarationAstContent> {
|
||||
override _astType: string = "VariableDeclaration";
|
||||
override _doSerialization(context: RuntimeContext, opts?: { global: boolean }): string {
|
||||
if (context.currentFunctionInfo) {
|
||||
context.currentFunctionInfo.localDeclaration.push(this);
|
||||
@@ -656,6 +682,7 @@ export class PrecisionAstNode extends AstNode<IPrecisionAstContent> {
|
||||
}
|
||||
|
||||
export class ShaderPropertyDeclareAstNode extends AstNode<IShaderPropertyDeclareAstContent> {
|
||||
override _astType: string = "ShaderProperty";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return `uniform ${this.content.prefix?.serialize(context) ?? ""} ${this.content.declare.serialize(context)};`;
|
||||
}
|
||||
@@ -671,7 +698,9 @@ export class DeclarationWithoutAssignAstNode extends AstNode<IDeclarationWithout
|
||||
}
|
||||
}
|
||||
|
||||
export class StructAstNode extends AstNode<IStructAstContent> {}
|
||||
export class StructAstNode extends AstNode<IStructAstContent> {
|
||||
override _astType: string = "Struct";
|
||||
}
|
||||
|
||||
export class PassPropertyAssignmentAstNode extends AstNode<IPassPropertyAssignmentAstContent> {}
|
||||
|
||||
@@ -682,6 +711,7 @@ export class TagAssignmentAstNode extends AstNode<ITagAssignmentAstContent> {
|
||||
}
|
||||
|
||||
export class TagAstNode extends AstNode<ITagAstContent> {
|
||||
override _astType: string = "Tag";
|
||||
override getContentValue(context?: RuntimeContext) {
|
||||
const ret = {} as IShaderPassInfo["tags"];
|
||||
for (const t of this.content) {
|
||||
@@ -698,6 +728,7 @@ export class TupleNumber3AstNode extends AstNode<ITupleNumber3> {}
|
||||
export class TupleNumber2AstNode extends AstNode<ITupleNumber2> {}
|
||||
|
||||
export class CullModeAstNode extends AstNode<ICullModeAstContent> {
|
||||
override _astType: string = "CullMode";
|
||||
override getContentValue() {
|
||||
const prop = this.content.split(".")[1];
|
||||
return CullMode[prop];
|
||||
@@ -705,6 +736,7 @@ export class CullModeAstNode extends AstNode<ICullModeAstContent> {
|
||||
}
|
||||
|
||||
export class BlendFactorAstNode extends AstNode<IBlendFactorAstContent> {
|
||||
override _astType: string = "BlendFactor";
|
||||
override getContentValue() {
|
||||
const prop = this.content.split(".")[1];
|
||||
return BlendFactor[prop];
|
||||
@@ -712,6 +744,7 @@ export class BlendFactorAstNode extends AstNode<IBlendFactorAstContent> {
|
||||
}
|
||||
|
||||
export class BlendOperationAstNode extends AstNode<IBlendOperationAstContent> {
|
||||
override _astType: string = "BlendOperation";
|
||||
override getContentValue() {
|
||||
const prop = this.content.split(".")[1];
|
||||
return BlendOperation[prop];
|
||||
@@ -719,6 +752,7 @@ export class BlendOperationAstNode extends AstNode<IBlendOperationAstContent> {
|
||||
}
|
||||
|
||||
export class StencilOperationAstNode extends AstNode<IStencilOperationAstContent> {
|
||||
override _astType: string = "StencilOperation";
|
||||
override getContentValue() {
|
||||
const prop = this.content.split(".")[1];
|
||||
return StencilOperation[prop];
|
||||
@@ -726,6 +760,7 @@ export class StencilOperationAstNode extends AstNode<IStencilOperationAstContent
|
||||
}
|
||||
|
||||
export class CompareFunctionAstNode extends AstNode<ICompareFunctionAstContent> {
|
||||
override _astType: string = "CompareFunction";
|
||||
override getContentValue() {
|
||||
const prop = this.content.split(".")[1];
|
||||
return CompareFunction[prop];
|
||||
@@ -733,6 +768,7 @@ export class CompareFunctionAstNode extends AstNode<ICompareFunctionAstContent>
|
||||
}
|
||||
|
||||
export class RenderQueueValueAstNode extends AstNode<IRenderQueueAstContent> {
|
||||
override _astType: string = "RenderQueue";
|
||||
isVariable: boolean;
|
||||
|
||||
override getContentValue() {
|
||||
@@ -745,6 +781,7 @@ export class RenderQueueValueAstNode extends AstNode<IRenderQueueAstContent> {
|
||||
export class RenderQueueAssignmentAstNode extends AstNode<IRuleRenderQueueAssignmentAstContent> {}
|
||||
|
||||
export class ForLoopAstNode extends AstNode<IForLoopAstContent> {
|
||||
override _astType: string = "ForLoop";
|
||||
override _doSerialization(context?: RuntimeContext, args?: any): string {
|
||||
return `for (${this.content.init.serialize(context)} ${this.content.condition.serialize(
|
||||
context
|
||||
|
||||
@@ -162,9 +162,8 @@ export interface IFnConditionStatementAstContent {
|
||||
}
|
||||
|
||||
export interface IConditionExprAstContent {
|
||||
leftExpr: RelationExprAstNode;
|
||||
rightExpr?: RelationExprAstNode;
|
||||
operator?: RelationOperatorAstNode;
|
||||
expressionList: RelationExprAstNode[];
|
||||
operatorList?: RelationOperatorAstNode[];
|
||||
}
|
||||
|
||||
export interface IFnRelationExprAstContent {
|
||||
@@ -227,6 +226,8 @@ export type IVariablePropertyAstContent = string;
|
||||
|
||||
export type IFnReturnStatementAstContent = ObjectAstNode;
|
||||
|
||||
export type IFnCallStatementAstContent = FnCallAstNode;
|
||||
|
||||
export interface IFnArgAstContent {
|
||||
name: string;
|
||||
type: {
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export { ShaderLab } from "./ShaderLab";
|
||||
export { ShaderVisitor } from "./ShaderVisitor";
|
||||
export { ShaderParser } from "./parser/ShaderParser";
|
||||
|
||||
//@ts-ignore
|
||||
export const version = `__buildVersion`;
|
||||
|
||||
console.log(`Galacean ShaderLab version: ${version}`);
|
||||
|
||||
@@ -343,9 +343,14 @@ export class ShaderParser extends CstParser {
|
||||
this.CONSUME(Symbols.Semicolon);
|
||||
});
|
||||
|
||||
private _ruleFnCallStatement = this.RULE("_ruleFnCallStatement", () => {
|
||||
this.SUBRULE(this._ruleFnCall);
|
||||
this.CONSUME(Symbols.Semicolon);
|
||||
});
|
||||
|
||||
private _ruleFnStatement = this.RULE("_ruleFnStatement", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnCall) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnCallStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnReturnStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnAssignStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnVariableDeclaration) },
|
||||
@@ -354,7 +359,8 @@ export class ShaderParser extends CstParser {
|
||||
{ ALT: () => this.SUBRULE(this._ruleBreakStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleContinueStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleForLoopStatement) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFn) }
|
||||
{ ALT: () => this.SUBRULE(this._ruleFn) },
|
||||
{ ALT: () => this.SUBRULE(this._ruleFnBlockStatement) }
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -434,7 +440,7 @@ export class ShaderParser extends CstParser {
|
||||
|
||||
private _ruleConditionExpr = this.RULE("_ruleConditionExpr", () => {
|
||||
this.SUBRULE(this._ruleFnRelationExpr);
|
||||
this.OPTION(() => {
|
||||
this.MANY(() => {
|
||||
this.SUBRULE(this._ruleRelationOperator);
|
||||
this.SUBRULE1(this._ruleFnRelationExpr);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Lexer, createToken } from "chevrotain";
|
||||
|
||||
export const Identifier = createToken({ name: "Identifier", pattern: /[a-zA-Z][_a-zA-Z0-9]*/ });
|
||||
export const Identifier = createToken({ name: "Identifier", pattern: /[_a-zA-Z][_a-zA-Z0-9]*/ });
|
||||
|
||||
export const WhiteSpace = createToken({
|
||||
name: "WhiteSpace",
|
||||
|
||||
@@ -500,13 +500,23 @@ export type _ruleContinueStatementCstChildren = {
|
||||
Semicolon: IToken[];
|
||||
};
|
||||
|
||||
export interface _ruleFnCallStatementCstNode extends CstNode {
|
||||
name: "_ruleFnCallStatement";
|
||||
children: _ruleFnCallStatementCstChildren;
|
||||
}
|
||||
|
||||
export type _ruleFnCallStatementCstChildren = {
|
||||
_ruleFnCall: _ruleFnCallCstNode[];
|
||||
Semicolon: IToken[];
|
||||
};
|
||||
|
||||
export interface _ruleFnStatementCstNode extends CstNode {
|
||||
name: "_ruleFnStatement";
|
||||
children: _ruleFnStatementCstChildren;
|
||||
}
|
||||
|
||||
export type _ruleFnStatementCstChildren = {
|
||||
_ruleFnCall?: _ruleFnCallCstNode[];
|
||||
_ruleFnCallStatement?: _ruleFnCallStatementCstNode[];
|
||||
_ruleFnReturnStatement?: _ruleFnReturnStatementCstNode[];
|
||||
_ruleFnAssignStatement?: _ruleFnAssignStatementCstNode[];
|
||||
_ruleFnVariableDeclaration?: _ruleFnVariableDeclarationCstNode[];
|
||||
@@ -516,6 +526,7 @@ export type _ruleFnStatementCstChildren = {
|
||||
_ruleContinueStatement?: _ruleContinueStatementCstNode[];
|
||||
_ruleForLoopStatement?: _ruleForLoopStatementCstNode[];
|
||||
_ruleFn?: _ruleFnCstNode[];
|
||||
_ruleFnBlockStatement?: _ruleFnBlockStatementCstNode[];
|
||||
};
|
||||
|
||||
export interface _ruleFnAssignStatementCstNode extends CstNode {
|
||||
@@ -1190,6 +1201,7 @@ export interface ICstNodeVisitor<IN, OUT> extends ICstVisitor<IN, OUT> {
|
||||
_ruleDiscardStatement(children: _ruleDiscardStatementCstChildren, param?: IN): OUT;
|
||||
_ruleBreakStatement(children: _ruleBreakStatementCstChildren, param?: IN): OUT;
|
||||
_ruleContinueStatement(children: _ruleContinueStatementCstChildren, param?: IN): OUT;
|
||||
_ruleFnCallStatement(children: _ruleFnCallStatementCstChildren, param?: IN): OUT;
|
||||
_ruleFnStatement(children: _ruleFnStatementCstChildren, param?: IN): OUT;
|
||||
_ruleFnAssignStatement(children: _ruleFnAssignStatementCstChildren, param?: IN): OUT;
|
||||
_ruleForLoopStatement(children: _ruleForLoopStatementCstChildren, param?: IN): OUT;
|
||||
|
||||
@@ -205,4 +205,14 @@ describe("ShaderLab", () => {
|
||||
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/noFragArgs.shader")).toString();
|
||||
glslValidate(demoShader, shaderLab);
|
||||
});
|
||||
|
||||
it("water full shader(complex)", () => {
|
||||
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/waterfull.shader")).toString();
|
||||
glslValidate(demoShader, shaderLab);
|
||||
});
|
||||
|
||||
it("glass shader", () => {
|
||||
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/glass.shader")).toString();
|
||||
glslValidate(demoShader, shaderLab);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from "chai";
|
||||
import { ShaderLab } from "@galacean/engine-shader-lab";
|
||||
import { Shader } from "@galacean/engine-core";
|
||||
import { Shader, ShaderFactory } from "@galacean/engine-core";
|
||||
import { ISubShaderInfo } from "@galacean/engine-design";
|
||||
|
||||
function addLineNum(str: string) {
|
||||
@@ -35,16 +35,16 @@ function validateShaderPass(pass: ISubShaderInfo["passes"][number]) {
|
||||
const vs = gl.createShader(gl.VERTEX_SHADER);
|
||||
const fs = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
|
||||
const fsPrefix = `precision mediump float;
|
||||
const fsPrefix = `#version 300 es\nprecision mediump float;
|
||||
precision mediump int;
|
||||
`;
|
||||
const fsSource = fsPrefix + pass.fragmentSource;
|
||||
const vsSource = "precision mediump float;\n" + pass.vertexSource;
|
||||
const vsSource = "#version 300 es\nprecision mediump float;\n" + pass.vertexSource;
|
||||
|
||||
gl.shaderSource(vs, vsSource);
|
||||
gl.shaderSource(vs, ShaderFactory.convertTo300(vsSource));
|
||||
gl.compileShader(vs);
|
||||
|
||||
gl.shaderSource(fs, fsSource);
|
||||
gl.shaderSource(fs, ShaderFactory.convertTo300(fsSource, true));
|
||||
gl.compileShader(fs);
|
||||
|
||||
expect(
|
||||
|
||||
114
tests/src/shader-lab/shaders/glass.shader
Normal file
114
tests/src/shader-lab/shaders/glass.shader
Normal file
@@ -0,0 +1,114 @@
|
||||
Shader "Gem" {
|
||||
SubShader "Default" {
|
||||
Pass "0" {
|
||||
struct a2v {
|
||||
vec4 POSITION;
|
||||
vec2 TEXCOORD_0;
|
||||
vec3 NORMAL;
|
||||
vec4 TANGENT;
|
||||
}
|
||||
|
||||
struct v2f {
|
||||
vec2 v_uv;
|
||||
vec3 v_posWS;
|
||||
vec3 v_normalWS;
|
||||
}
|
||||
|
||||
mat4 camera_VPMat;
|
||||
mat4 renderer_ModelMat;
|
||||
mat4 renderer_NormalMat;
|
||||
|
||||
VertexShader = vert;
|
||||
|
||||
v2f vert( a2v attr ) {
|
||||
v2f vary;
|
||||
|
||||
vec3 posWS = ( renderer_ModelMat * attr.POSITION ).xyz;
|
||||
vec3 NormalWS = normalize( mat3( renderer_NormalMat ) * attr.NORMAL.xyz );
|
||||
|
||||
vec4 posCS = camera_VPMat * vec4( posWS, 1.0 );
|
||||
|
||||
vary.v_uv = attr.TEXCOORD_0;
|
||||
vary.v_posWS = posWS;
|
||||
vary.v_normalWS = NormalWS;
|
||||
|
||||
gl_Position = posCS;
|
||||
return v2f;
|
||||
}
|
||||
|
||||
vec3 camera_Position;
|
||||
mat4 camera_ViewMat;
|
||||
vec4 _RefractColor;
|
||||
vec4 _Thinkmap_ST;
|
||||
vec4 _DirtyMask_ST;
|
||||
vec4 _Decal_ST;
|
||||
float _Min;
|
||||
float _Max;
|
||||
float _DirtyIntensity;
|
||||
float _RefractIntensity;
|
||||
float _ReflectFresnelBias;
|
||||
float _ReflectFresnelScale;
|
||||
float _ReflectFresnelPower;
|
||||
float _ReflectIntensity;
|
||||
|
||||
sampler2D _ReflectMatcap;
|
||||
sampler2D _RefractMatcap;
|
||||
sampler2D _Thinkmap;
|
||||
sampler2D _DirtyMask;
|
||||
sampler2D _Decal;
|
||||
|
||||
vec2 getMatcapUV( mat4 viewMatrix, vec3 worldNormal ) {
|
||||
return( ( ( mat3( viewMatrix ) * worldNormal ).xy ) * 0.5 ) + 0.5;
|
||||
}
|
||||
|
||||
FragmentShader = frag;
|
||||
|
||||
void frag( v2f vary ) {
|
||||
vec3 posWS = vary.v_posWS;
|
||||
vec3 normalWS = vary.v_normalWS;
|
||||
|
||||
vec3 viewDirWS = normalize( camera_Position - posWS );
|
||||
|
||||
vec2 MarCapUV = getMatcapUV( camera_ViewMat, normalWS );
|
||||
// 反射
|
||||
vec4 reflectCol = texture2D( _ReflectMatcap, MarCapUV );
|
||||
|
||||
// 厚度
|
||||
float dotResult = dot( normalWS, viewDirWS );
|
||||
float smoothstepResult = smoothstep( _Min, _Max, dotResult );
|
||||
|
||||
vec2 uv_Thinkmap = vary.v_uv * _Thinkmap_ST.xy + _Thinkmap_ST.zw;
|
||||
vec4 thinkMapPixel = texture2D( _Thinkmap, uv_Thinkmap );
|
||||
float tmp_thinkness = thinkMapPixel.r;
|
||||
|
||||
// 污渍
|
||||
vec2 uv_DirtyMask = vary.v_uv * _DirtyMask_ST.xy + _DirtyMask_ST.zw;
|
||||
vec4 dirtyPixel = texture2D( _DirtyMask, uv_DirtyMask );
|
||||
float dirtyCol = _DirtyIntensity * dirtyPixel.r;
|
||||
|
||||
float Thinkness = clamp( ( ( 1.0 - smoothstepResult ) + tmp_thinkness + dirtyCol ), 0.0, 1.0 );
|
||||
|
||||
// 折射
|
||||
float temp_output4 = ( Thinkness * _RefractIntensity );
|
||||
vec4 refractCol = mix( _RefractColor * 0.5, _RefractColor * texture2D( _RefractMatcap, MarCapUV + temp_output4 ), temp_output4 );
|
||||
|
||||
vec4 temp_output5 = ( reflectCol + refractCol );
|
||||
|
||||
// 贴花
|
||||
vec2 uv_Decal = vary.v_uv * _Decal_ST.xy + _Decal_ST.zw;
|
||||
vec4 decalCol = texture2D( _Decal, uv_Decal );
|
||||
vec4 temp_output6 = mix( temp_output5, decalCol, decalCol.a );
|
||||
float decalAlpha = decalCol.a;
|
||||
|
||||
// 计算菲涅尔
|
||||
float fresnelNdotV = dot( normalWS, viewDirWS );
|
||||
float fresnel = ( _ReflectFresnelBias + _ReflectFresnelScale * pow( 1.0 - fresnelNdotV, _ReflectFresnelPower ) );
|
||||
|
||||
float alpha = clamp( ( decalAlpha + max( ( fresnel * reflectCol.r * _ReflectIntensity ), Thinkness ) ), 0.0, 1.0 );
|
||||
vec3 color = temp_output6.rgb;
|
||||
|
||||
gl_FragColor = vec4( color, alpha );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
399
tests/src/shader-lab/shaders/waterfull.shader
Normal file
399
tests/src/shader-lab/shaders/waterfull.shader
Normal file
@@ -0,0 +1,399 @@
|
||||
Shader "Waterfull" {
|
||||
EditorProperties { // 颜色
|
||||
_ShallowColor( "shallow color", Color ) = ( 0.4858, 1, 0.86, 1.0 );
|
||||
_DeepColor( "deep color", Color ) = ( 0, 0.4673, 0, 1 );
|
||||
_WaterDeep( "water deep", Range( 3, 8, 0.1 ) ) = 5;
|
||||
_FresnelColor( "fresnel color", Color ) = ( 0.788, 0.89, 1, 1 );
|
||||
_FresnelIntensity( "fresnel intensity", Float ) = 0;
|
||||
_ReflectionAngle( "reflection angle", Float ) = 0.3;
|
||||
_ShoreDistance( "shore distance", Float ) = 0.6;
|
||||
_Alpha( "alpha", Float ) = 0.6;
|
||||
_DayIntensity( "_DayIntensity", Float ) = 0.39;
|
||||
// 法线
|
||||
_WaterNormalSmall( "water normal small", Texture2D );
|
||||
_SmallNormalTiling( "small normal tiling", Float ) = 1.4;
|
||||
_SmallNormalSpeed( "small normal speed", Float ) = 0.38;
|
||||
_SmallNormalIntensity( "small normal intensity", Float ) = 0.15;
|
||||
_WaterNormalLarge( "water normal large", Texture2D );
|
||||
_LargeNormalTiling( "large normal tiling", Float ) = 1.22;
|
||||
_LargeNormalSpeed( "large normal speed", Float ) = 1.0;
|
||||
_LargeNormalIntensity( "large normal intensity", Float ) = 0.08;
|
||||
// 反射
|
||||
_ReflectCube( "relect cube", TextureCube );
|
||||
_ReflectDistort( "relect distort", Float ) = 0.3;
|
||||
_ReflectIntensity( "reflect intensity", Float ) = 1.18;
|
||||
// 焦散
|
||||
_CausticsTex( "caustics texture", Texture2D );
|
||||
_CausticsScale( "caustic scanle", Float ) = 10;
|
||||
_CausticsSpeed( "caustic speed", Vector2 ) = ( -9, 0 );
|
||||
_CausticsIntensity( "caustic intensity", Float ) = 0.8;
|
||||
// Foam
|
||||
_FoamNoise( "foam noise", Texture2D );
|
||||
_XTilling( "x tiling", Float ) = 1.0;
|
||||
_YTilling( "y tiling", Float ) = 1.0;
|
||||
_FoamNoiseSpeed( "foam noise speed", Vector2 ) = ( 0, -0.3 );
|
||||
_FoamOffset( "foam offset", Float ) = 0.42;
|
||||
_FoamRange( "foam range", Float ) = 0.5;
|
||||
_FoamColor( "foam color", Color ) = ( 1, 1, 1, 1 );
|
||||
// 波光
|
||||
_SparklesIntensity( "sparkles intensity", Float ) = 5;
|
||||
_SparklesAmount( "sparkles amount", Float ) = 0.5;
|
||||
// 顶点波浪
|
||||
_Direction( "direction", Vector2 ) = ( 1, 1 );
|
||||
_WaveSpeed( "wave speed", Float ) = 0.5;
|
||||
_WaveDistance( "wave distance", Float ) = 0.288;
|
||||
_WaveHeight( "wave height", Float ) = 0.1;
|
||||
_SubWaveDirection( "sub wave direction", Vector4 ) = ( 0.1, 0.1, 0.1, 0.1 );
|
||||
_WaveNormalStr( "wave normal strength", Float ) = 0.16;
|
||||
_WaveFadeStart( "wave fade start", Int ) = 200;
|
||||
_WaveFadeEnd( "wave fade end", Int ) = 500;
|
||||
_WaveColor( "wave color", Color ) = ( 0, 4, 3.75, 1 );
|
||||
}
|
||||
SubShader "sub1" {
|
||||
Pass "p1" {
|
||||
mat4 camera_VPMat;
|
||||
mat4 camera_ViewInvMat;
|
||||
vec4 scene_ElapsedTime;
|
||||
float _WaveSpeed;
|
||||
vec2 _Direction;
|
||||
vec4 _SubWaveDirection;
|
||||
float _WaveDistance;
|
||||
float _WaveHeight;
|
||||
float _WaveNormalStr;
|
||||
float _WaveFadeStart;
|
||||
float _WaveFadeEnd;
|
||||
mat4 camera_ProjMat;
|
||||
mat4 renderer_ModelMat;
|
||||
mat4 renderer_NormalMat;
|
||||
vec3 camera_Position;
|
||||
struct a2v {
|
||||
vec3 POSITION;
|
||||
vec2 TEXCOORD_0;
|
||||
vec3 NORMAL;
|
||||
vec4 TANGENT;
|
||||
}
|
||||
struct v2f {
|
||||
mat4 v_ProjectionInvMat;
|
||||
vec2 v_uv;
|
||||
float v_waveY;
|
||||
vec3 v_posOS;
|
||||
vec3 v_posWS;
|
||||
vec4 v_posCS;
|
||||
vec3 v_normalOS;
|
||||
vec3 v_defaultNormalWS;
|
||||
vec3 v_defaultTangentWS;
|
||||
vec3 v_defaultBinormalWS;
|
||||
}
|
||||
vec3 GerstnerOffset4( vec2 xzVtx, vec4 steepness, vec4 amp, vec4 freq, vec4 speed, vec4 dirAB, vec4 dirCD ) {
|
||||
vec3 offsets;
|
||||
vec4 AB = steepness.xxyy * dirAB.xyzw * amp.xxyy;
|
||||
vec4 CD = steepness.zzww * dirCD.xyzw * amp.zzww;
|
||||
vec4 dotABCD = freq.xyzw * vec4( dot( dirAB.xy, xzVtx ), dot( dirAB.zw, xzVtx ), dot( dirCD.xy, xzVtx ), dot( dirCD.zw, xzVtx ) );
|
||||
vec4 COS = cos( dotABCD + speed );
|
||||
vec4 SIN = sin( dotABCD + speed );
|
||||
offsets.x = dot( COS, vec4( AB.xz, CD.xz ) );
|
||||
offsets.z = dot( COS, vec4( AB.yw, CD.yw ) );
|
||||
offsets.y = dot( SIN, amp ); // Remap to only positive values;
|
||||
return offsets;
|
||||
}
|
||||
vec3 GerstnerNormal4( vec2 xzVtx, vec4 amp, vec4 freq, vec4 speed, vec4 dirAB, vec4 dirCD, float normalStr ) {
|
||||
vec3 nrml = vec3( 0, 2.0, 0 );
|
||||
vec4 AB = freq.xxyy * amp.xxyy * dirAB.xyzw;
|
||||
vec4 CD = freq.zzww * amp.zzww * dirCD.xyzw;
|
||||
vec4 dotABCD = freq.xyzw * vec4( dot( dirAB.xy, xzVtx ), dot( dirAB.zw, xzVtx ), dot( dirCD.xy, xzVtx ), dot( dirCD.zw, xzVtx ) );
|
||||
vec4 COS = cos( dotABCD + speed );
|
||||
nrml.x -= dot( COS, vec4( AB.xz, CD.xz ) );
|
||||
nrml.z -= dot( COS, vec4( AB.yw, CD.yw ) );
|
||||
nrml.xz *= normalStr;
|
||||
nrml = normalize( nrml );
|
||||
return nrml;
|
||||
}
|
||||
void Gerstner( vec3 offs, vec3 nrml, vec2 position, vec4 amplitude, vec4 frequency, vec4 steepness, vec4 speed, vec4 directionAB, vec4 directionCD, float normalStr ) {
|
||||
offs += GerstnerOffset4( position, steepness, amplitude, frequency, speed, directionAB, directionCD );
|
||||
// #ifdef CALCULATE_NORMALS
|
||||
nrml += GerstnerNormal4( position, amplitude, frequency, speed, directionAB, directionCD, normalStr );
|
||||
// #endif
|
||||
}
|
||||
#define WAVE_COUNT 2
|
||||
#define MAX_WAVE_COUNT 5
|
||||
#define STEEPNESS_SCALE 0.01
|
||||
// v1.1.8+
|
||||
void GetWaveInfo( vec2 position, vec2 time, vec4 directionABCD, float wavedistance, float height, float normalStr, float fadeStart, float fadeEnd, vec3 positionWSOffset, vec3 normalWS ) {
|
||||
vec3 positionOffset = vec3( 0.0, 0.0, 0.0 );
|
||||
vec3 normal = vec3( 0.0, 0.0, 0.0 );
|
||||
vec4 amp = vec4( 0.3, 0.35, 0.25, 0.25 );
|
||||
vec4 freq = vec4( 1.3, 1.35, 1.25, 1.25 ) * ( 1.0 - wavedistance ) * 3.0;
|
||||
vec4 speed = vec4( 1.2 * time.x, 1.375 * time.y, 1.1 * time.x, time.y ); // Pre-multiplied with time
|
||||
vec4 dir1 = vec4( 0.3, 0.85, 0.85, 0.25 ) * directionABCD;
|
||||
vec4 dir2 = vec4( 0.1, 0.9, -0.5, -0.5 ) * directionABCD;
|
||||
// vec4 steepness = vec4(12.0, 12.0, 12.0, 12.0) * _WaveSteepness * lerp(1.0, MAX_WAVE_COUNT, 1/WAVE_COUNT);
|
||||
vec4 steepness = vec4( 0.0, 0.0, 0.0, 0.0 );
|
||||
// Distance based scalar
|
||||
float pixelDist = length( camera_Position.xz - position.xy );
|
||||
float fadeFactor = clamp( ( fadeEnd - pixelDist ) / ( fadeEnd - fadeStart ), 0.0, 1.0 );
|
||||
for ( int i = 0; i <= WAVE_COUNT; i ++ ) {
|
||||
float t = 1.0 + ( float( i ) / float( WAVE_COUNT ) );
|
||||
freq *= t;
|
||||
amp *= fadeFactor;
|
||||
Gerstner( positionOffset, normal, position, amp, freq, steepness, speed, dir1, dir2, normalStr );
|
||||
}
|
||||
normalWS = normalize( normal );
|
||||
// Average
|
||||
positionOffset.y /= float( WAVE_COUNT );
|
||||
positionOffset.xz *= STEEPNESS_SCALE * height;
|
||||
positionOffset.y *= height;
|
||||
positionWSOffset = positionOffset;
|
||||
}
|
||||
v2f vert( a2v v ) {
|
||||
v2f out;
|
||||
out.v_ProjectionInvMat = inverse( camera_ProjMat );
|
||||
mat4 WorldToObjectMat = inverse( renderer_ModelMat );
|
||||
out.v_uv = v.TEXCOORD_0;
|
||||
vec3 defaultPosWS = ( renderer_ModelMat * vec4( v.POSITION, 1.0 ) ).xyz;
|
||||
vec3 defaultNormalWS = normalize( mat3( renderer_NormalMat ) * v.NORMAL.xyz );
|
||||
vec3 defaultTangentWS = normalize( mat3( renderer_NormalMat ) * v.TANGENT.xyz );
|
||||
vec3 defaultBinormalWS = cross( defaultNormalWS, defaultTangentWS ) * v.TANGENT.w;
|
||||
// #ifdef _VERTEXWAVE_ON
|
||||
vec3 positionWSOffset = vec3( 0.0, 0.0, 0.0 );
|
||||
vec3 normalWS = vec3( 0.0, 0.0, 0.0 );
|
||||
GetWaveInfo( defaultPosWS.xz, scene_ElapsedTime.x * _WaveSpeed * _Direction, _SubWaveDirection, _WaveDistance, _WaveHeight, _WaveNormalStr, _WaveFadeStart, _WaveFadeEnd, positionWSOffset, normalWS );
|
||||
vec3 waveVertexPos = ( WorldToObjectMat * vec4( positionWSOffset + defaultPosWS, 1.0 ) ).xyz;
|
||||
float waveY = positionWSOffset.y;
|
||||
vec3 waveVertexNormal = normalize( ( WorldToObjectMat * vec4( normalWS, 0.0 ) ).xyz );
|
||||
// #else
|
||||
// vec3 NormalWS = normalize( mat3( renderer_NormalMat ) * v.NORMAL.xyz );
|
||||
// vec3 waveVertexPos = POSITION;
|
||||
// float waveY = 0.0;
|
||||
// vec3 waveVertexNormal = NORMAL;
|
||||
// #endif
|
||||
vec3 posWS = ( renderer_ModelMat * vec4( waveVertexPos, 1.0 ) ).xyz;
|
||||
vec4 posCS = camera_VPMat * vec4( posWS, 1.0 );
|
||||
out.v_waveY = waveY;
|
||||
out.v_posOS = waveVertexPos;
|
||||
out.v_posWS = posWS;
|
||||
out.v_posCS = posCS;
|
||||
out.v_normalOS = waveVertexNormal;
|
||||
out.v_defaultNormalWS = defaultNormalWS;
|
||||
out.v_defaultBinormalWS = defaultBinormalWS;
|
||||
out.v_defaultTangentWS = defaultTangentWS;
|
||||
gl_Position = posCS;
|
||||
return out;
|
||||
}
|
||||
VertexShader = vert;
|
||||
vec4 scene_ElapsedTime;
|
||||
sampler2D camera_DepthTexture;
|
||||
vec3 _ShallowColor;
|
||||
vec3 _DeepColor;
|
||||
float _WaterDeep;
|
||||
vec3 _FresnelColor;
|
||||
float _FresnelIntensity;
|
||||
float _ReflectionAngle;
|
||||
float _ShoreDistance;
|
||||
float _Alpha;
|
||||
float _DayIntensity;
|
||||
sampler2D _WaterNormalSmall;
|
||||
float _SmallNormalTiling;
|
||||
float _SmallNormalSpeed;
|
||||
float _SmallNormalIntensity;
|
||||
sampler2D _WaterNormalLarge;
|
||||
float _LargeNormalTiling;
|
||||
float _LargeNormalSpeed;
|
||||
float _LargeNormalIntensity;
|
||||
samplerCube _ReflectCube;
|
||||
float _ReflectDistort;
|
||||
float _ReflectIntensity;
|
||||
sampler2D _CausticsTex;
|
||||
float _CausticsScale;
|
||||
vec2 _CausticsSpeed;
|
||||
float _CausticsIntensity;
|
||||
sampler2D _FoamNoise;
|
||||
float _XTilling;
|
||||
float _YTilling;
|
||||
vec2 _FoamNoiseSpeed;
|
||||
float _FoamOffset;
|
||||
float _FoamRange;
|
||||
vec3 _FoamColor;
|
||||
float _SparklesIntensity;
|
||||
float _SparklesAmount;
|
||||
vec3 _WaveColor;
|
||||
vec3 BlendNormal( vec3 n1, vec3 n2 ) {
|
||||
return normalize( vec3( n1.xy * n2.z + n2.xy * n1.z, n1.z * n2.z ) );
|
||||
}
|
||||
void frag( v2f i ) {
|
||||
vec3 posWS = i.v_posWS;
|
||||
vec3 posOS = i.v_posOS;
|
||||
vec4 posCS = i.v_posCS;
|
||||
vec3 posCSNDC = posCS.xyz / posCS.w;
|
||||
vec3 normalOS = normalize( i.v_normalOS );
|
||||
vec3 defaultNormalWS = normalize( i.v_defaultNormalWS );
|
||||
vec3 defaultTangentWS = normalize( i.v_defaultTangentWS );
|
||||
vec3 defaultBinormalWS = normalize( i.v_defaultBinormalWS );
|
||||
vec3 tanToWorld0 = vec3( defaultTangentWS.x, defaultBinormalWS.x, defaultNormalWS.x );
|
||||
vec3 tanToWorld1 = vec3( defaultTangentWS.y, defaultBinormalWS.y, defaultNormalWS.y );
|
||||
vec3 tanToWorld2 = vec3( defaultTangentWS.z, defaultBinormalWS.z, defaultNormalWS.z );
|
||||
vec3 viewDirWS = normalize( camera_Position - posWS );
|
||||
float WaterDepth = 0.0;
|
||||
vec3 UnderwaterPosWS = vec3( 0.0, 0.0, 0.0 );
|
||||
{
|
||||
vec2 screenUV = posCSNDC.xy * 0.5 + 0.5;
|
||||
vec4 texel = texture2D( camera_DepthTexture, screenUV );
|
||||
float depth = texel.r;
|
||||
vec3 underwaterPosNDC = vec3( screenUV, depth ) * 2.0 - 1.0;
|
||||
vec4 underwaterPosWSFromDepth = camera_ViewInvMat * v_ProjectionInvMat * vec4( underwaterPosNDC, 1.0 );
|
||||
UnderwaterPosWS = underwaterPosWSFromDepth.xyz / underwaterPosWSFromDepth.w;
|
||||
WaterDepth = posWS.y - UnderwaterPosWS.y;
|
||||
WaterDepth *= 1.0;
|
||||
}
|
||||
vec3 SurfaceNormal = vec3( 0.0, 0.0, 1.0 );
|
||||
{
|
||||
vec3 SmallNormalData = vec3( 0.0, 0.0, 1.0 );
|
||||
{
|
||||
vec2 uv = i.v_uv * _SmallNormalTiling;
|
||||
float offset = ( _SmallNormalSpeed * scene_ElapsedTime.x * 0.1 );
|
||||
vec3 smallNormalData = vec3( 0.0, 0.0, 0.0 );
|
||||
vec2 uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
vec4 pixel = texture2D( _WaterNormalSmall, uv1 );
|
||||
smallNormalData = pixel.xyz * 2.0 - 1.0;
|
||||
#ifdef _WATERQULIATY_MID
|
||||
uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
vec2 uv2 = ( offset * vec2( -0.1, -0.1 ) ) + uv + 0.4;
|
||||
vec4 pixel1 = texture2D( _WaterNormalSmall, uv1 );
|
||||
vec4 pixel2 = texture2D( _WaterNormalSmall, uv2 );
|
||||
vec3 smallNormalData1 = pixel1.xyz * 2.0 - 1.0;
|
||||
vec3 smallNormalData2 = pixel2.xyz * 2.0 - 1.0;
|
||||
smallNormalData = BlendNormal( smallNormalData1, smallNormalData2 );
|
||||
#endif
|
||||
#ifdef _WATERQULIATY_HIGH
|
||||
vec2 uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
vec2 uv2 = ( offset * vec2( -0.1, -0.1 ) ) + uv + 0.4;
|
||||
vec2 uv3 = ( offset * vec2( -0.1, 0.1 ) + ( uv + vec2( 0.85, 0.15 ) ) );
|
||||
vec2 uv4 = ( offset * vec2( 0.1, -0.1 ) + ( uv + vec2( 0.65, 0.75 ) ) );
|
||||
vec4 pixel1 = texture2D( _WaterNormalSmall, uv1 );
|
||||
vec4 pixel2 = texture2D( _WaterNormalSmall, uv2 );
|
||||
vec4 pixel3 = texture2D( _WaterNormalSmall, uv3 );
|
||||
vec4 pixel4 = texture2D( _WaterNormalSmall, uv4 );
|
||||
vec3 smallNormalData1 = pixel1.xyz * 2.0 - 1.0;
|
||||
vec3 smallNormalData2 = pixel2.xyz * 2.0 - 1.0;
|
||||
vec3 smallNormalData3 = pixel3.xyz * 2.0 - 1.0;
|
||||
vec3 smallNormalData4 = pixel4.xyz * 2.0 - 1.0;
|
||||
smallNormalData = BlendNormal( smallNormalData1, smallNormalData2 );
|
||||
smallNormalData = BlendNormal( smallNormalData, smallNormalData3 );
|
||||
smallNormalData = BlendNormal( smallNormalData, smallNormalData4 );
|
||||
#endif
|
||||
SmallNormalData = mix( vec3( 0.0, 0.0, 1.0 ), smallNormalData, _SmallNormalIntensity );
|
||||
}
|
||||
vec3 LargeNormalData = vec3( 0.0, 0.0, 1.0 );
|
||||
{
|
||||
vec2 uv = i.v_uv * _LargeNormalTiling;
|
||||
float offset = ( _LargeNormalSpeed * scene_ElapsedTime.x * 0.1 );
|
||||
vec3 largeNormalData = vec3( 0.0, 0.0, 0.0 );
|
||||
vec2 uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
vec4 pixel1 = texture2D( _WaterNormalLarge, uv1 );
|
||||
largeNormalData = pixel1.xyz * 2.0 - 1.0;
|
||||
#ifdef _WATERQULIATY_MID
|
||||
uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
vec2 uv2 = ( offset * vec2( -0.1, -0.1 ) ) + uv + 0.4;
|
||||
pixel1 = texture2D( _WaterNormalLarge, uv1 );
|
||||
vec4 pixel2 = texture2D( _WaterNormalLarge, uv2 );
|
||||
vec3 largeNormalData1 = pixel1.xyz * 2.0 - 1.0;
|
||||
vec3 largeNormalData2 = pixel2.xyz * 2.0 - 1.0;
|
||||
largeNormalData = BlendNormal( largeNormalData1, largeNormalData2 );
|
||||
#endif
|
||||
#ifdef _WATERQULIATY_HIGH
|
||||
uv1 = ( offset * vec2( 0.1, 0.1 ) ) + uv;
|
||||
uv2 = ( offset * vec2( -0.1, -0.1 ) ) + uv + 0.4;
|
||||
vec2 uv3 = ( offset * vec2( -0.1, 0.1 ) + ( uv + vec2( 0.85, 0.15 ) ) );
|
||||
vec2 uv4 = ( offset * vec2( 0.1, -0.1 ) + ( uv + vec2( 0.65, 0.75 ) ) );
|
||||
pixel1 = texture2D( _WaterNormalLarge, uv1 );
|
||||
vec4 pixel2 = texture2D( _WaterNormalLarge, uv2 );
|
||||
vec4 pixel3 = texture2D( _WaterNormalLarge, uv3 );
|
||||
vec4 pixel4 = texture2D( _WaterNormalLarge, uv4 );
|
||||
vec3 largeNormalData1 = pixel1.xyz * 2.0 - 1.0;
|
||||
vec3 largeNormalData2 = pixel2.xyz * 2.0 - 1.0;
|
||||
vec3 largeNormalData3 = pixel3.xyz * 2.0 - 1.0;
|
||||
vec3 largeNormalData4 = pixel4.xyz * 2.0 - 1.0;
|
||||
largeNormalData = BlendNormal( largeNormalData1, largeNormalData2 );
|
||||
largeNormalData = BlendNormal( largeNormalData, largeNormalData3 );
|
||||
largeNormalData = BlendNormal( largeNormalData, largeNormalData4 );
|
||||
#endif
|
||||
LargeNormalData = mix( vec3( 0.0, 0.0, 1.0 ), largeNormalData, _LargeNormalIntensity );
|
||||
}
|
||||
SurfaceNormal = normalize( BlendNormal( SmallNormalData, LargeNormalData ) );
|
||||
}
|
||||
// Fresnel
|
||||
float FresnelFactor = 0.0;
|
||||
float ReflectFresnel = 0.0;
|
||||
float ColorFresnel = 0.0;
|
||||
{
|
||||
vec3 SurfaceNormalWS = vec3( dot( tanToWorld0, SurfaceNormal ), dot( tanToWorld1, SurfaceNormal ), dot( tanToWorld2, SurfaceNormal ) );
|
||||
float NoV = dot( viewDirWS, SurfaceNormalWS );
|
||||
FresnelFactor = 1.0 - max( NoV, 0.0 );
|
||||
FresnelFactor = mix( 0.04, 1.0, pow( FresnelFactor, 5.0 ) ); // 限制最小值
|
||||
ReflectFresnel = pow( FresnelFactor, _ReflectionAngle );
|
||||
ColorFresnel = ( ReflectFresnel * _FresnelIntensity * 5.0 );
|
||||
ColorFresnel = clamp( ( ColorFresnel * ColorFresnel ), 0.0, 1.0 );
|
||||
}
|
||||
// Water Color
|
||||
float WaterDeepFresnelRange = 0.0;
|
||||
vec3 WaterColor = vec3( 0.0, 0.0, 0.0 );
|
||||
{
|
||||
vec3 deepFresnelColor = mix( _DeepColor, _FresnelColor, ColorFresnel );
|
||||
float waveY = clamp( i.v_waveY, 0.0, 1.0 );
|
||||
vec3 deepFresnelWaveColor = mix( deepFresnelColor, _WaveColor, waveY );
|
||||
float waterDeepRange = WaterDepth / _WaterDeep;
|
||||
WaterDeepFresnelRange = clamp( FresnelFactor + waterDeepRange, 0.0, 1.0 );
|
||||
WaterColor = mix( _ShallowColor, deepFresnelWaveColor, WaterDeepFresnelRange );
|
||||
}
|
||||
// Reflect
|
||||
vec3 ReflectColor = vec3( 0.0, 0.0, 0.0 );
|
||||
{
|
||||
vec3 reflectNormalTS = mix( vec3( 0, 0, 1 ), SurfaceNormal, _ReflectDistort );
|
||||
vec3 reflectNormalWS = vec3( dot( tanToWorld0, reflectNormalTS ), dot( tanToWorld1, reflectNormalTS ), dot( tanToWorld2, reflectNormalTS ) );
|
||||
vec3 reflectDirWS = reflect( -viewDirWS, reflectNormalWS );
|
||||
vec4 cube = textureCube( _ReflectCube, reflectDirWS );
|
||||
ReflectColor = cube.rgb * cube.a * 5.0;
|
||||
ReflectColor *= _ReflectIntensity * ReflectFresnel;
|
||||
}
|
||||
// Foam
|
||||
vec3 FoamColor = _FoamColor;
|
||||
float FoamAlpha = 0.0;
|
||||
{
|
||||
float foamDepth = clamp( WaterDepth / _FoamRange, 0.0, 1.0 );
|
||||
vec2 uv = scene_ElapsedTime.x * _FoamNoiseSpeed + vec2( _XTilling * v_uv.x, foamDepth * _YTilling );
|
||||
vec4 pixel = texture2D( _FoamNoise, uv );
|
||||
float foamNoise = pixel.r;
|
||||
float foamRange = 1.0 - clamp( ( _FoamOffset + foamDepth ), 0.0, 1.0 );
|
||||
foamRange = clamp( ( ( foamRange + 1.0 ) * step( foamNoise, foamRange ) ), 0.0, 1.0 );
|
||||
FoamColor = foamRange * _FoamColor * 2.0;
|
||||
FoamAlpha = foamRange;
|
||||
}
|
||||
// Caustics
|
||||
vec3 CausticsColor = vec3( 0.0, 0.0, 0.0 );
|
||||
{
|
||||
vec2 uv = UnderwaterPosWS.xz / _CausticsScale;
|
||||
vec2 offset = _CausticsSpeed * scene_ElapsedTime.x * 0.01;
|
||||
float waterShallowRange = 1.0 - WaterDeepFresnelRange;
|
||||
vec4 pixel1 = texture2D( _CausticsTex, uv + offset );
|
||||
vec4 pixel2 = texture2D( _CausticsTex, -uv + offset );
|
||||
CausticsColor = min( pixel1.rgb, pixel2.rgb );
|
||||
CausticsColor *= _CausticsIntensity * waterShallowRange;
|
||||
}
|
||||
// Splakes
|
||||
vec3 SplakesColor = vec3( 0.0, 0.0, 0.0 );
|
||||
{
|
||||
float Splakes = step( _SparklesAmount, SurfaceNormal.y ) * _SparklesIntensity;
|
||||
SplakesColor = Splakes * vec3( 1.0, 1.0, 1.0 );
|
||||
}
|
||||
vec3 FinalColor = mix( ( WaterColor + ReflectColor + CausticsColor + SplakesColor ) * _DayIntensity, FoamColor, FoamAlpha );
|
||||
// Alpha
|
||||
float waterOpacity = clamp( ( WaterDepth / _ShoreDistance ), 0.0, 1.0 );
|
||||
float otherOpacity = clamp( max( max( FoamAlpha, ReflectFresnel ), CausticsColor.r ), 0.0, 1.0 );
|
||||
float FinalAlpha = mix( waterOpacity, 1.0, otherOpacity );
|
||||
FinalAlpha = clamp( ( FinalAlpha * _Alpha ), 0.0, 1.0 );
|
||||
gl_FragColor = vec4( FinalColor, FinalAlpha );
|
||||
// gl_FragColor = vec4( FinalColor, 1.0 );
|
||||
}
|
||||
FragmentShader = frag;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user