diff --git a/packages/shader-lab/src/AstNodeUtils.ts b/packages/shader-lab/src/AstNodeUtils.ts index 1d6fef288..c7c109241 100644 --- a/packages/shader-lab/src/AstNodeUtils.ts +++ b/packages/shader-lab/src/AstNodeUtils.ts @@ -31,8 +31,8 @@ export class AstNodeUtils { static defaultVisit(this: ICstVisitor, ctx: CstChildrenDictionary): ObjectAstNode { const content = {} as Record; - 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; } diff --git a/packages/shader-lab/src/RuntimeContext.ts b/packages/shader-lab/src/RuntimeContext.ts index 1dd6083b4..ad060df82 100644 --- a/packages/shader-lab/src/RuntimeContext.ts +++ b/packages/shader-lab/src/RuntimeContext.ts @@ -58,6 +58,12 @@ interface IReferenceStructInfo { } export default class RuntimeContext { + private _shaderAst: AstNode; + + 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): 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, 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): IShaderPassInfo | string { - this._passReset(); - if (typeof ast.content === "string") { + // UsePass return ast.content; } + this._passReset(); + this._initPassGlobalList(>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(>ast, prop, ret, tmpRenderStates) ); for (const rs of tmpRenderStates) { diff --git a/packages/shader-lab/src/ShaderLab.ts b/packages/shader-lab/src/ShaderLab.ts index 955a1b675..dd176160f 100644 --- a/packages/shader-lab/src/ShaderLab.ts +++ b/packages/shader-lab/src/ShaderLab.ts @@ -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; } diff --git a/packages/shader-lab/src/ShaderVisitor.ts b/packages/shader-lab/src/ShaderVisitor.ts index 336be2317..9758abbf8 100644 --- a/packages/shader-lab/src/ShaderVisitor.ts +++ b/packages/shader-lab/src/ShaderVisitor.ts @@ -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 { const astInfo = this.visit(item); @@ -474,20 +476,16 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial 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 = AstNodeUtils.defaultVisit.bind(this)(children).content; return Object.values(ret)[0]; @@ -741,8 +749,8 @@ export class ShaderVisitor extends ShaderVisitorConstructor implements Partial { position: IPositionRange; content: T; - /** @internal */ - _isAstNode = true; + _astType = "unknown"; constructor(position: IPositionRange, content: T) { this.position = position; @@ -168,6 +168,7 @@ export class AstNode { } export class ReturnTypeAstNode extends AstNode { + override _astType = "ReturnType"; override _doSerialization(context: RuntimeContext): string { return this.content.text; } @@ -177,13 +178,14 @@ export class ObjectAstNode extends AstNode>> 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 { + override _astType: string = "Function"; override _doSerialization(context: RuntimeContext): string { context.functionAstStack.push({ fnAst: this, localDeclaration: [] }); @@ -220,6 +222,7 @@ export class FnAstNode extends AstNode { } export class FnBodyAstNode extends AstNode { + 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 { } export class FnMacroDefineAstNode extends AstNode { + 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 { + 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 { + 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 { }); } } - 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 { 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 { } export class NumberAstNode extends AstNode { + override _astType: string = "Number"; override _doSerialization(context: RuntimeContext): string { return this.content.text; } @@ -449,6 +462,7 @@ export class NumberAstNode extends AstNode { } export class BooleanAstNode extends AstNode { + override _astType: string = "Boolean"; override _doSerialization(context: RuntimeContext): string { return this.content.text; } @@ -459,6 +473,7 @@ export class BooleanAstNode extends AstNode { } export class FnVariableAstNode extends AstNode { + 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 { } export class FnArrayVariableAstNode extends AstNode { + override _astType: string = "ArrayVariable"; override _doSerialization(context?: RuntimeContext, args?: any): string { return this.content.variable; } @@ -513,7 +529,14 @@ export class FnReturnStatementAstNode extends AstNode { + override _doSerialization(context?: RuntimeContext, args?: any): string { + return `${this.content.serialize(context)};`; + } +} + export class FnArgAstNode extends AstNode { + 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 { } export class RenderStateDeclarationAstNode extends AstNode { + override _astType: string = "RenderState"; override getContentValue(context?: RuntimeContext): { variable: string; properties: IShaderPassInfo["renderStates"]; @@ -601,12 +625,14 @@ export class AssignableValueAstNode extends AstNode } } export class VariableTypeAstNode extends AstNode { + override _astType: string = "VariableType"; override _doSerialization(context: RuntimeContext): string { return this.content.text; } } export class VariableDeclarationAstNode extends AstNode { + 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 { } export class ShaderPropertyDeclareAstNode extends AstNode { + 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 {} +export class StructAstNode extends AstNode { + override _astType: string = "Struct"; +} export class PassPropertyAssignmentAstNode extends AstNode {} @@ -682,6 +711,7 @@ export class TagAssignmentAstNode extends AstNode { } export class TagAstNode extends AstNode { + 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 {} export class TupleNumber2AstNode extends AstNode {} export class CullModeAstNode extends AstNode { + override _astType: string = "CullMode"; override getContentValue() { const prop = this.content.split(".")[1]; return CullMode[prop]; @@ -705,6 +736,7 @@ export class CullModeAstNode extends AstNode { } export class BlendFactorAstNode extends AstNode { + override _astType: string = "BlendFactor"; override getContentValue() { const prop = this.content.split(".")[1]; return BlendFactor[prop]; @@ -712,6 +744,7 @@ export class BlendFactorAstNode extends AstNode { } export class BlendOperationAstNode extends AstNode { + override _astType: string = "BlendOperation"; override getContentValue() { const prop = this.content.split(".")[1]; return BlendOperation[prop]; @@ -719,6 +752,7 @@ export class BlendOperationAstNode extends AstNode { } export class StencilOperationAstNode extends AstNode { + override _astType: string = "StencilOperation"; override getContentValue() { const prop = this.content.split(".")[1]; return StencilOperation[prop]; @@ -726,6 +760,7 @@ export class StencilOperationAstNode extends AstNode { + override _astType: string = "CompareFunction"; override getContentValue() { const prop = this.content.split(".")[1]; return CompareFunction[prop]; @@ -733,6 +768,7 @@ export class CompareFunctionAstNode extends AstNode } export class RenderQueueValueAstNode extends AstNode { + override _astType: string = "RenderQueue"; isVariable: boolean; override getContentValue() { @@ -745,6 +781,7 @@ export class RenderQueueValueAstNode extends AstNode { export class RenderQueueAssignmentAstNode extends AstNode {} export class ForLoopAstNode extends AstNode { + override _astType: string = "ForLoop"; override _doSerialization(context?: RuntimeContext, args?: any): string { return `for (${this.content.init.serialize(context)} ${this.content.condition.serialize( context diff --git a/packages/shader-lab/src/ast-node/AstNodeContent.ts b/packages/shader-lab/src/ast-node/AstNodeContent.ts index 3f837ff48..9f5025804 100644 --- a/packages/shader-lab/src/ast-node/AstNodeContent.ts +++ b/packages/shader-lab/src/ast-node/AstNodeContent.ts @@ -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: { diff --git a/packages/shader-lab/src/index.ts b/packages/shader-lab/src/index.ts index f51a35c74..650000eea 100644 --- a/packages/shader-lab/src/index.ts +++ b/packages/shader-lab/src/index.ts @@ -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}`); diff --git a/packages/shader-lab/src/parser/ShaderParser.ts b/packages/shader-lab/src/parser/ShaderParser.ts index 3fd82ce5c..93f0b693d 100644 --- a/packages/shader-lab/src/parser/ShaderParser.ts +++ b/packages/shader-lab/src/parser/ShaderParser.ts @@ -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); }); diff --git a/packages/shader-lab/src/parser/tokens/Other.ts b/packages/shader-lab/src/parser/tokens/Other.ts index b31f6d97b..de21b31cd 100644 --- a/packages/shader-lab/src/parser/tokens/Other.ts +++ b/packages/shader-lab/src/parser/tokens/Other.ts @@ -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", diff --git a/packages/shader-lab/src/types.ts b/packages/shader-lab/src/types.ts index cd0a10808..a223cdc18 100644 --- a/packages/shader-lab/src/types.ts +++ b/packages/shader-lab/src/types.ts @@ -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 extends ICstVisitor { _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; diff --git a/tests/src/shader-lab/ShaderLab.test.ts b/tests/src/shader-lab/ShaderLab.test.ts index bc3baa55f..d08e8633d 100644 --- a/tests/src/shader-lab/ShaderLab.test.ts +++ b/tests/src/shader-lab/ShaderLab.test.ts @@ -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); + }); }); diff --git a/tests/src/shader-lab/ShaderValidate.ts b/tests/src/shader-lab/ShaderValidate.ts index bb9582890..11c803720 100644 --- a/tests/src/shader-lab/ShaderValidate.ts +++ b/tests/src/shader-lab/ShaderValidate.ts @@ -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( diff --git a/tests/src/shader-lab/shaders/glass.shader b/tests/src/shader-lab/shaders/glass.shader new file mode 100644 index 000000000..2c23a2c75 --- /dev/null +++ b/tests/src/shader-lab/shaders/glass.shader @@ -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 ); + } + } + } +} \ No newline at end of file diff --git a/tests/src/shader-lab/shaders/waterfull.shader b/tests/src/shader-lab/shaders/waterfull.shader new file mode 100644 index 000000000..36bb1e79c --- /dev/null +++ b/tests/src/shader-lab/shaders/waterfull.shader @@ -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; + } + } +} \ No newline at end of file