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:
SwayYan
2023-11-24 18:35:00 +08:00
committed by GitHub
parent 99f03aa9e4
commit ea72dde3bf
14 changed files with 672 additions and 56 deletions

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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: {

View File

@@ -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}`);

View File

@@ -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);
});

View File

@@ -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",

View File

@@ -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;

View File

@@ -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);
});
});

View File

@@ -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(

View 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 );
}
}
}
}

View 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;
}
}
}