DefaultMaterial
The default material is the approach to centralize basic shader operations, like:
- vertex transformation
- displacement mapping
- handling of multiple light sources
- handling of different light types: directional, spot or point light
- texture tiling
- normal mapping
- parallax occlusion mapping
- various shading models: lambert, phong, blinn-phong, PBR
- plane clipping
- color clipping
- color computation by ambient, diffuse, specular and emissive color
- point sprite rendering (particle effects)
- fog
- shadowing: shadow mapping or variant shadow mapping
- weighted blended order independent transparency
- reflection, refraction and environment mapping
The simple reason is that those functionality is used in nearly all materials and should not be reinvented each time. Those operations also getting very complex in combination and its hard to keep them still efficient.
Node-Based Shaders
The default material uses a node based memory structure to build shader source code. This means that each shader (vertex- and fragmentshader) holds a nodes hierarchy from which the shader source code is generated from.
Source code getting build by a TGorillaNodeBuilder instance, which each shader provides. The attached nodes are TGorillaNodeEntity instances or inherited from that class. When the shader is getting compiled, the ToString() method is getting called for each node in hierarchy. Depending on the activated defines, the node itself decides how its output code looks like.
This is a very flexible way and allows to modify or replace specific nodes, to produce your own shader code.
On rendering, the source code will automatically be compiled and registered in GPU.
Default Vertex Shader
The default vertex shader is setting up the following node hierarchy.
Root * SourceCode [TGorillaCompositeNode] - <0> [TGorillaGLSLPragma] - Globals [TGorillaGlobalsNode] - Constants [TGorillaCompositeNode] - Types [TGorillaCompositeNode] - <0> [TGorillaGLSLLightType] // DEACTIVATED - <1> [TGorillaGLSLTextureType] // DEACTIVATED - <2> [TGorillaGLSLMaterialType] // DEACTIVATED - <3> [TGorillaGLSLPassDataType] - <4> [TGorillaGLSLVertexShaderLocalsStructNode] - Attributes [TGorillaCompositeNode] - <0> [TGorillaGLSLAttributesNode] - Uniforms [TGorillaUniformsNode] - <0> [TGorillaGLSLUBOShaderData] - Varying [TGorillaCompositeNode] - <0> [TGorillaGLSLInOutNode] - Functions [TGorillaCompositeNode] - <0> [TGorillaGLSLTransformFuncsNode] - <1> [TGorillaGLSLGetTextureFuncsNode] - <2> [TGorillaVertexShaderFuncNode] - Variables [TGorillaCompositeNode] - EntryPoint [TGorillaGLSLMainNode] - <0> [TGorillaGLSLVertexShaderLocalsNode] - <1> [TGorillaGLSLMatricesOutNode] - <2> [TGorillaGLSLGetVertexPositionNode] - <3> [TGorillaGLSLDisplacementMappingGetNode] - <4> [TGorillaGLSLDisplacementMappingApplyNode] - <5> [TGorillaGLSLClippingPlaneGetNode] - <6> [TGorillaGLSLVertexShaderOutputNode] - <7> [TGorillaGLSLAdjustPointSizeNode] - SurfaceShader [TGorillaSurfaceShaderNode] - <0> [TGorillaGLSLVertexShaderExec] - <1> [TGorillaGLSLVertexPositionOutputNode]
Default Fragment Shader
The default fragment shader is setting up the following node hierarchy.
Root * SourcCode [TGorillaCompositeNode] - <0> [TGorillaGLSLPragma] - Globals [TGorillaGlobalsNode] - Constants [TGorillaCompositeNode] - Types [TGorillaCompositeNode] - <0> [TGorillaGLSLLightType] - <1> [TGorillaGLSLTextureType] - <2> [TGorillaGLSLMaterialType] - <3> [TGorillaGLSLPassDataType] - <4> [TGorillaGLSLFragmentShaderLocalsStructNode] - Attributes [TGorillaCompositeNode] - Uniforms [TGorillaUniformsNode] - <0> [TGorillaGLSLUBOShaderData] - Varying [TGorillaCompositeNode] - <0> [TGorillaGLSLInOutNode] - Functions [TGorillaCompositeNode] - <0> [TGorillaGLSLPackingFuncsNode] - <1> [TGorillaGLSLGetDirLightDirectionFuncNode] - <2> [TGorillaGLSLGetPointLightDirectionFuncNode] - <3> [TGorillaGLSLGetSpotLightDirectionFuncNode] - <4> [TGorillaGLSLGetLightDirectionFuncNode] - <5> [TGorillaGLSLGetSpotFalloffFuncNode] - <6> [TGorillaGLSLGetLightAttenuationFuncNode] - <7> [TGorillaGLSLGetShadingFuncsNode] - <8> [TGorillaGLSLGetTextureFuncsNode] - <9> [TGorillaGLSLGetTextureAtlasFuncNode] - <10> [TGorillaGLSLBasicShadowFuncsNode] - <11> [TGorillaGLSLFaketracingShadowFuncsNode] // DEPRECATED - <12> [TGorillaGLSLBumpFuncsNode] - <13> [TGorillaGLSLPOMFuncsNode] - <14> [TGorillaGLSLPBRFuncsNode] - <15> [TGorillaGLSLFogFuncNode] - <16> [TGorillaSurfaceShaderFuncNode] - Variables [TGorillaCompositeNode] - <0> [TGorillaGLSLLayoutLocation] (o_Albedo) - <1> [TGorillaGLSLLayoutLocation] (o_Alpha) - <2> [TGorillaGLSLLayoutLocation] (o_Position) - <3> [TGorillaGLSLLayoutLocation] (o_Normals) - <4> [TGorillaGLSLLayoutLocation] (o_Components) // DEFERRED_RENDERING ONLY - <5> [TGorillaGLSLLayoutLocation] (o_Ambient) // DEFERRED_RENDERING ONLY - <6> [TGorillaGLSLLayoutLocation] (o_Emissive) // DEFERRED_RENDERING ONLY - <7> [TGorillaGLSLLayoutLocation] (o_Specular) // DEFERRED_RENDERING ONLY - EntryPoint [TGorillaGLSLMainNode] - <0> [TGorillaGLSLClippingPlaneNode] - <1> [TGorillaGLSLMatricesOutNode] - <2> [TGorillaGLSLLightingLocalsNode] - <3> [TGorillaGLSLSpecularColorNode] - <4> [TGorillaGLSLGetTransfVertPosNode] - <5> [TGorillaGLSLGetNormalNode] - <6> [TGorillaGLSLGetFragViewDirNode] - <7> [TGorillaGLSLTextureCoordsNode] - <8> [TGorillaGLSLPOMApplyNode] - <9> [TGorillaGLSLGetBumpNormalNode] - <10> [TGorillaGLSLPBRPrepareNode] - <11> [TGorillaGLSLLightsLoopNode] - <0> [TGorillaGLSLGetLightShadingNode] - <1> [TGorillaGLSLLightingSumNode] - <2> [TGorillaGLSLAddShadowNode] - <2> [TGorillaGLSLAddFaketracingShadowNode] // DEPRECATED - <12> [TGorillaGLSLPBRAmbientNode] - SurfaceShader [TGorillaSurfaceShaderNode] - <0> [TGorillaGLSLGetBaseColorNode] - <1> [TGorillaGLSLSpecularMapNode] - <2> [TGorillaGLSLAddEnvironmentNode] - <3> [TGorillaGLSLAddSparseVoxelsNode] - <4> [TGorillaGLSLAddReflectionNode] - <5> [TGorillaGLSLAddRefractionNode] - <6> [TGorillaGLSLSurfaceShaderExec] - <7> [TGorillaGLSLMergedColorOutputNode] - <8> [TGorillaGLSLBrightnessColorNode] - <9> [TGorillaGLSLFragColorOutputNode] - <10> [TGorillaGLSLColorClipNode] - <11> [TGorillaGLSLFogApplyNode] - <12> [TGorillaGLSLWeightedBlendedOITNode]
Node Classes
Class | Description |
---|---|
TGorillaNodeEntity | The basic node class for all other node classes. The ToString() method returns a source code snippet. The result of all nodes will produce the final shader code. |
TGorillaCompositeNode | Is the basic node, which can hold sub-nodes and outputs those in the ToString() method |
TGorillaUniformsNode | Is the basic node for input variables, which is overwritten in the specific shader language |
TGorillaGlobalsNode | Is the storage point for global variables, constants, attributes, types, inputs, in/out variables and functions |
TGorillaDeclarationNode | Is the basic node for declaring types or functions |
TGorillaTypeNode | Is inherited from TGorillaDeclarationNode and represents a type declaration in shader source code |
TGorillaStructTypeNode | Is inherited from TGorillaTypeNode and represents a structured type declaration in shader source code |
TGorillaVariableNode | Is inherited from TGorillaDeclarationNode and represents a variable declaration in a type or as global / local. |
TGorillaConstantNode | Is a simple entity to declare a constant value |
TGorillaOperationNode | Allows to build mathmatical or logical expressions in node format. |
TGorillaStatementNode | Basic node to hold a number of operation nodes, which delimits the line on output, f.e. by a semicolon. |
TGorillaReferenceNode | A node instance with a reference to another node. This is used by syntax nodes to build mathmatical or logical expressions in node format. |
TGorillaForLoopNode | A node which will output a for-loop statement. |
TGorillaSurfaceInputNode | Structure type definition node used by the TGorillaSurfaceShaderNode as input parameter. |
TGorillaSurfaceOutputNode | Structured type definition node used by the TGorillaSurfaceShaderNode as output parameter. |
TGorillaVertexShaderFuncNode | Defines the empty void VertexShader(inout TLocals DATA){} function declaration for integrating DesignTime Shader functionality |
TGorillaSurfaceShaderFuncNode | Defines the empty void SurfaceShader(inout TLocals DATA){} function declaration for integrating DesignTime Shader functionality |
TGorillaGLSLVertexShaderExec | Node to call user specific vertex shader function for DesignTime functionality. |
TGorillaGLSLSurfaceShaderExec | Node to call user specific fragment shader function for DesignTime functionality. |
TGorillaSurfaceShaderNode | This node type is intended to be the entry point for user specific source code implementation. |
TGorillaEntryPointNode | Defines the shader source code entry point. On GLSL this is the main() function (the first function called, when a shader gets executed) |
GLSL Node Classes
In the following we list the most common GLSL shader nodes. This should help to understand where you can manipulate or influence shader source code compilation.
Top-Level Nodes
At the top-level of a shader code tree either TGorillaGLSLVertexShader or TGorillaGLSLFragmentShader is attached to the node builder instance.
Class | Description |
---|---|
TGorillaGLSLVertexShader | The basic vertex shader node builder type |
TGorillaGLSLFragmentShader | The basic fragment shader node builder type |
Second-Level Nodes
Attached to one of those nodes are the following container nodes:
Class | Description |
---|---|
TGorillaGLSLPragma | Pragma definition node, f.e. to define the GLSL version: #version 310 and float/int precisions. |
TGorillaGlobalsNode | Containing global declarations, f.e. for variables or functions. |
TGorillaGLSLMainNode | Containing the main function code for GLSL shaders. |
TGorillaGlobalsNode (Globals)
A TGorillaGlobalsNode node automatically provides sub-containers for structuring, f.e. variables, types, functions, …
The following list is showing which node type should be inserted into which sub-container.
Sub-Container | Class | Description |
---|---|---|
Constants | TGorillaGLSLConstant | Create an instance of this class for your own constant declaration and insert it into the Constants sub-container. |
- | - | - |
Variables | TGorillaGLSLVariable | Create an instance of this class for your own global variable declaration and insert it into the Variables sub-container. |
- | - | - |
Types | TGorillaGLSLStructType | Extend this class for your own structure declaration and insert it into the Types sub-container. |
Types | TGorillaGLSLLightType | Defines the internal light structure type. It is not recommend to change it. |
Types | TGorillaGLSLTextureType | Defines the internal texture structure type. It is not recommend to change it. |
Types | TGorillaGLSLMaterialType | Defines the internal material structure type. It is not recommend to change it. |
Types | TGorillaGLSLPassDataType | Defines the internal pass variable structure type. It is not recommend to change it. |
Types | TGorillaGLSLVertexShaderLocalsStructNode | Defines the TLOCALS structure with all pre-defined properties:
Values inside of TLOCALS will be applied at the end of the shader to the varying parameters. It's recommended to modify those values instead of varying values directly (could be overwritten again). |
Types | TGorillaGLSLFragmentShaderLocalsStructNode | Defines the TLOCALS structure with all pre-defined properties:
Values inside of TLOCALS will be applied at the end of the shader to the fragment output. It's recommended to modify those values instead of output values directly (could be overwritten again). NOTICE: values getting filled up during computation and are not set at all times. |
- | - | - |
Uniforms | TGorillaGLSLUBOShaderData | Defines the internal uniform buffer object (v3) or variable declaration (v2). |
Uniforms | TGorillaGLSLUniformsNode | Create an instance of this class, if you need additional input variables in your shader |
- | - | - |
Attributes | TGorillaGLSLAttributesNode | Holds the available vertex attributes. It is not recommend to change this node, if you are not sure if the additional attributes are provided by the mesh data.
|
- | - | - |
Varying | TGorillaGLSLInOutNode | Holds the input/output variables (varying) used by each shader. On GLES v3+ everything is encapsulated in the INOUT structure accessable by: vars.* Available varying values are:
|
- | - | - |
Functions | TGorillaDeclarationNode | Extend the declaration node for a user-specific function node and attach it to the Functions property of the Globals node. |
Functions | TGorillaGLSLTransformFuncsNode | Provides functions for vertex transformation:
|
Functions | TGorillaGLSLPackingFuncsNode | Provides function declarations for packaging / unpackaging:
|
Functions | TGorillaGLSLGetDirLightDirectionFuncNode | Providing function declarations for retrieving light direction depending on the supplied light type. |
Functions | TGorillaGLSLGetPointLightDirectionFuncNode | Providing function declarations for retrieving light direction for point lights |
Functions | TGorillaGLSLGetSpotLightDirectionFuncNode | Providing function declarations for retrieving light direction for spot lights |
Functions | TGorillaGLSLGetLightDirectionFuncNode | Providing function declarations for retrieving light direction for directional lights |
Functions | TGorillaGLSLGetSpotFalloffFuncNode | Providing function declarations for computing spot light fall off. |
Functions | TGorillaGLSLGetLightAttenuationFuncNode | Providing function declarations for computing light attenuation depending on the light type. |
Functions | TGorillaGLSLGetShadingFuncsNode | Providing function declarations for computing lambert, phong or blinn-phong shading . |
Functions | TGorillaGLSLGetTextureFuncsNode | Provides function declarations for:
|
Functions | TGorillaGLSLGetTextureAtlasFuncNode | Providing function declarations for extracting specific frames from texture atlas. |
Functions | TGorillaGLSLBasicShadowFuncsNode | Providing shadow computation functions for shadow mapping or variance shadow mapping depending on the material settings. |
Functions | TGorillaGLSLFaketracingShadowFuncsNode | DEPRECATED: A bad approach for better shadow computation. |
Functions | TGorillaGLSLBumpFuncsNode | Providing function declarations for bump mapping and normal mapping. |
Functions | TGorillaGLSLPOMFuncsNode | Providing function declaration for parallax occlusion mapping functionality |
Functions | TGorillaGLSLPBRFuncsNode | Providing function declaration for physically based rendering functionality. |
Functions | TGorillaGLSLFogFuncNode | Provididing fog computation function declarations. |
Functions | TGorillaVertexShaderFuncNode | By default it's providing an empty function: void VertexShader(inout TLocals DATA){} which can be filled up by users at designtime or runtime. |
Functions | TGorillaSurfaceShaderFuncNode | By default it's providing an empty function: void SurfaceShader(inout TLocals DATA){} which can be filled up by users at designtime or runtime. |
- | - | - |
- | TGorillaGLSLLayoutLocation | Defines a layout location for render target output. This node is only available for modern OpenGL 4.3+ or OpenGLES 3.1+ syntax. In the default fragment shader the following layouts are automatically registered:
|
TGorillaGLSLMainNode (EntryPoint)
Vertex Shader
Class | Description | |
---|---|---|
- | TGorillaGLSLVertexShaderLocalsNode | Empty node. Variable declaration was moved to TGorillaGLSLVertexShaderLocalsStructNode. |
- | TGorillaGLSLMatricesOutNode | DEPRECATED: Pushes all transformation matrices to varying parameters (OpenGLES v2):
. While in modern OpenGL (GLES v3.1+) everything is already stored in the uniform buffer. |
- | TGorillaGLSLGetVertexPositionNode | Automatically sets the following LOCALS properties by attributes and uniforms: LOCALS.Position, LOCALS.ClippingPlane and LOCALS.Displace |
- | TGorillaGLSLDisplacementMappingGetNode | Requests LOCALS.Displace value from _DisplacementMapTexture. |
- | TGorillaGLSLDisplacementMappingApplyNode | Adds LOCALS.Displace to LOCALS.Position for vertex displacement. |
- | TGorillaGLSLClippingPlaneGetNode | Sets the locals value for LOCALS.ClippingPlane. |
- | TGorillaGLSLVertexShaderOutputNode | Automatically sets the following locals and varying parameters
|
- | TGorillaGLSLAdjustPointSizeNode | Computes sprite distance and sets gl_PointSize value. |
- | TGorillaSurfaceShaderNode | Sets up a container node SurfaceShader for surface computation, where further sub-nodes are attached to (TGorillaGLSLVertexShaderExec, TGorillaGLSLVertexPositionOutputNode). |
- | - | - |
SurfaceShader | TGorillaGLSLVertexShaderExec | Calls VertexShader(LOCALS);. |
SurfaceShader | TGorillaGLSLVertexPositionOutputNode | Sets varying values from TLOCALS structure for:
|
Fragment Shader
Class | Description | |
---|---|---|
- | TGorillaGLSLClippingPlaneNode | If GORILLA_GLSL_DEFINE_CLIPPINGPLANE is activated, it checks if clipping plane is activated and discards the fragment in case of clipping. |
- | TGorillaGLSLMatricesOutNode | Empty node and only compiled for vertex shader. |
- | TGorillaGLSLLightingLocalsNode | Empty node. Source code was moved to TGorillaGLSLFragmentShaderLocalsStructNode. |
- | TGorillaGLSLSpecularColorNode | If GORILLA_GLSL_DEFINE_USE_SPECULAR is activated, it sets LOCALS.SpecularColor from Material.Specular settings, otherwise the value is set to zero. |
- | TGorillaGLSLGetTransfVertPosNode | Sets LOCALS.TransfVertPos from vars.v_TransfVertPos and LOCALS.FragDepth from gl_FragCoord.z |
- | TGorillaGLSLGetNormalNode | If GORILLA_GLSL_DEFINE_BUMP is activated, it sets LOCALS.Normal from vars.v_TBN[2] |
- | TGorillaGLSLGetFragViewDirNode | Computes LOCALS.FragViewDir from direction between camera position and transformed vertex position. |
- | TGorillaGLSLTextureCoordsNode | If GORILLA_GLSL_DEFINE_USE_TEXTURE is activated, it sets LOCALS.TexCoord0 from vars.v_TexCoord0 and automatically applies tiling from first texture in material settings. |
- | TGorillaGLSLPOMApplyNode | If GORILLA_GLSL_DEFINE_POM is activated, it computes LOCALS.TexCoord0 by ParallaxOcclusionMapping() function. |
- | TGorillaGLSLGetBumpNormalNode | If GORILLA_GLSL_DEFINE_LIGHTING, GORILLA_GLSL_DEFINE_NORMALMAP and GORILLA_GLSL_DEFINE_BUMP are activated, it computes LOCALS.Normal by CalcBumpedNormal() function. |
- | TGorillaGLSLPBRPrepareNode | If GORILLA_GLSL_DEFINE_LIGHTING and GORILLA_GLSL_DEFINE_PBR are activated, it prepares values for physically based rendering. |
- | TGorillaGLSLLightsLoopNode | Sets up a loop for all available light sources inside of _Light[x]. Further node will be attached to it. Those subnodes will be executed for each light source. |
LightsLoop | TGorillaGLSLGetLightShadingNode | If GORILLA_GLSL_DEFINE_LIGHTING is activated the node will compute shading depending on the shading model: lambert, phong, blinn-phong or PBR. |
LightsLoop | TGorillaGLSLLightingSumNode | If GORILLA_GLSL_DEFINE_LIGHTING is activated the node will sum up lighting. If GORILLA_GLSL_DEFINE_PBR is activated it will output no code. If GORILLA_GLSL_DEFINE_USE_SPECULAR is activated, it will also apply specular lighting. |
LightsLoop | TGorillaGLSLAddShadowNode | If GORILLA_GLSL_DEFINE_LIGHTING and GORILLA_GLSL_DEFINE_SHADOW are activated, it will calculate LOCALS.ShadowSum depending on the GORILLA_GLSL_DEFINE_SHADOW_SM or GORILLA_GLSL_DEFINE_SHADOW_VSM define. |
LightsLoop | TGorillaGLSLAddFaketracingShadowNode | DEPRECATED: This node is deprecated and should not be used anymore. |
- | - | - |
- | TGorillaGLSLPBRAmbientNode | If GORILLA_GLSL_DEFINE_LIGHTING and GORILLA_GLSL_DEFINE_PBR are activated, it will set LOCALS.Ambient and LOCALS.Specular from albedo, AO and specular settings. |
- | TGorillaSurfaceShaderNode | Is a container node for the final surface color computation. |
SurfaceShader | TGorillaGLSLGetBaseColorNode | It will compute LOCALS.BaseColor and LOCALS.Alpha depending on GORILLA_GLSL_DEFINE_BUMP, GORILLA_GLSL_DEFINE_PBR, GORILLA_GLSL_DEFINE_USE_TEXTURE_ATLAS, GORILLA_GLSL_DEFINE_USE_POINTSPRITE, GORILLA_GLSL_DEFINE_LIGHTING , GORILLA_GLSL_DEFINE_USE_TEXTURE and GORILLA_GLSL_DEFINE_USE_VERTEXCOLOR defines. The node is way to complex to explain it's functionality here. |
SurfaceShader | TGorillaGLSLSpecularMapNode | If GORILLA_GLSL_DEFINE_SPECULARMAP is activated, it computes LOCALS.SpecularColor from _SpecularMapTexture. |
SurfaceShader | TGorillaGLSLAddEnvironmentNode | If GORILLA_GLSL_DEFINE_ENVIRONMENT is activated, it computes LOCALS.BaseColor and LOCALS.Ambient values from _EnvironmentTexture |
SurfaceShader | TGorillaGLSLAddReflectionNode | If GORILLA_GLSL_DEFINE_REFLECTION is activated, it merges _ReflectionTexture with LOCALS.BaseColor. |
SurfaceShader | TGorillaGLSLAddRefractionNode | If GORILLA_GLSL_DEFINE_REFRACTION is activated, it merges _RefractionTexture with LOCALS.BaseColor. |
SurfaceShader | TGorillaGLSLSurfaceShaderExec | Calls SurfaceShader(LOCALS); |
SurfaceShader | TGorillaGLSLMergedColorOutputNode | It will compute the final LOCALS.SumColor depending if GORILLA_GLSL_DEFINE_LIGHTING, GORILLA_GLSL_DEFINE_SPECULARMAP, GORILLA_GLSL_DEFINE_SHADOW or GORILLA_GLSL_DEFINE_PBR are activated. |
SurfaceShader | TGorillaGLSLBrightnessColorNode | It will multiply _Brightness value with LOCALS.SumColor (since v0.8.4.2322) |
SurfaceShader | TGorillaGLSLFragColorOutputNode | It will apply material alpha value to LOCALS.Alpha and finally write vec4(LOCALS.SumColor, LOCALS.Alpha) to o_Albedo render target. |
SurfaceShader | TGorillaGLSLColorClipNode | If GORILLA_GLSL_DEFINE_USE_COLORCLIP is activated, it will discard the fragment if threshold was reached. |
SurfaceShader | TGorillaGLSLFogApplyNode | It will compute fog value by calling computeFog() and modify the o_Albedo render target. |
SurfaceShader | TGorillaGLSLWeightedBlendedOITNode | It will perform WBOIT value computation and sets all other render target outputs
|
Using Defines
Not all nodes are enabled by default, so you have to unlock them by using the define management of the node builder. The following defines are provided in the default material:
Define | Notice |
---|---|
GORILLA_GLSL_DEFINE_VERTEXSHADER | Defines the specific nodebuilder as vertex shader source code |
GORILLA_GLSL_DEFINE_FRAGMENTSHADER | Defines the specific nodebuilder as fragment shader source code |
GORILLA_GLSL_DEFINE_DEPTH | Activates the depth map functionality. Nevertheless a depth render pass needs to be attached to the material |
GORILLA_GLSL_DEFINE_DEFERRED | NOT IMPLEMENTATED YET. DO NOT USE! |
GORILLA_GLSL_DEFINE_SHADOW | Activates shadow casting. The GORILLA_GLSL_DEFINE_DEPTH needs to be activated too. |
GORILLA_GLSL_DEFINE_SHADOW_SM | Activates simple shadow mapping. The GORILLA_GLSL_DEFINE_SHADOW needs to be activated too. |
GORILLA_GLSL_DEFINE_SHADOW_VSM | Activates variance shadow mapping. The GORILLA_GLSL_DEFINE_SHADOW needs to be activated too. |
GORILLA_GLSL_DEFINE_SHADOW_FT | DEPRECATED! DO NOT USE ANYMORE! |
GORILLA_GLSL_DEFINE_REFLECTION | Activates reflection handling. |
GORILLA_GLSL_DEFINE_REFRACTION | Activates refraction handling. |
GORILLA_GLSL_DEFINE_ENVIRONMENT | Activates enviroment mapping for reflection and refraction. Do not combine with GORILLA_GLSL_DEFINE_REFLECTION or GORILLA_GLSL_DEFINE_REFRACTION |
GORILLA_GLSL_DEFINE_CLIPPINGPLANE | Activates clipping plane handling. |
GORILLA_GLSL_DEFINE_NORMALMAP | Activates normal mapping functionality. |
GORILLA_GLSL_DEFINE_BUMP | Activates bump mapping functionality in combination with GORILLA_GLSL_DEFINE_NORMALMAP |
GORILLA_GLSL_DEFINE_POM | Activates parallax occlusion mapping computation |
GORILLA_GLSL_DEFINE_POM_DISCARD | Activates discarding edge fragments during parallax occlusion mapping. GORILLA_GLSL_DEFINE_POM needs to be activated. |
GORILLA_GLSL_DEFINE_SPECULARMAP | Allows to supply a specular texture for model specific specular color computation. |
GORILLA_GLSL_DEFINE_DISPLACEMENT | Enables the displacement mapping functionality in the vertex shader. |
GORILLA_GLSL_DEFINE_LIGHTING | Enables lighting computation with lambert, phong or blinnphong algorithm. |
GORILLA_GLSL_DEFINE_PHONG | Enables phong shading source code for the material |
GORILLA_GLSL_DEFINE_BLINNPHONG | Enables blinn-phong shading source code for the material |
GORILLA_GLSL_DEFINE_PBR | Enables physically based rendering source code for the material. |
GORILLA_GLSL_DEFINE_PBR_TEXTURE0 | Activates usage of _Texture0 in shader. If not activated, it expects LOCALS.BaseColor to be already set |
GORILLA_GLSL_DEFINE_PBR_TEXTURES | Enables usage of all PBR textures (Metalness, Roughness, AmbientOcclusion). Otherwise the bias values will be used *MetallicBias* and *RoughnessBias* |
GORILLA_GLSL_DEFINE_USE_SPECULAR | Enables specular light computation. |
GORILLA_GLSL_DEFINE_USE_VERTEXCOLOR | Enables to use the color of each vertex. |
GORILLA_GLSL_DEFINE_USE_TEXTURE | Enables general texture functionality (tex2D, tex2DProj, … methods) |
GORILLA_GLSL_DEFINE_USE_TEXTURE0 | Enables the Texture0 handling in the material |
GORILLA_GLSL_DEFINE_USE_TEXTURE_ATLAS | Declares texture atlas mapping functionality: GetTextureAtlasColor() |
GORILLA_GLSL_DEFINE_USE_POINTSPRITE | In combination with GORILLA_GLSL_DEFINE_USE_TEXTURE_ATLAS the Texture0 will be used as atlas and mapped onto each vertex point: GetTextureAtlasColor() |
GORILLA_GLSL_DEFINE_PACKED_NORMAL | Enables packed normals |
GORILLA_GLSL_DEFINE_INOUTVARS | Enables in/out variables for GLSLv3+ |
GORILLA_GLSL_DEFINE_VARYINGSTRUCTS | Enables varying structs. This define manages backwards compatibility |
GORILLA_GLSL_DEFINE_DEPRECATED | DO NOT USE ANYMORE! |
GORILLA_GLSL_DEFINE_INTEL | Automatically set by the system, in case of an Intel graphics card. |
GORILLA_GLSL_DEFINE_AMD | Automatically set by the system, in case of an AMD graphics card. |
GORILLA_GLSL_DEFINE_NVIDIA | Automatically set by the system, in case of an NVidia graphics card. |
GORILLA_GLSL_DEFINE_GEFORCE | Automatically set by the system, in case of an GeForce graphics card. |
GORILLA_GLSL_DEFINE_RADEON | Automatically set by the system, in case of an Radeon graphics card. |
GORILLA_GLSL_DEFINE_HDGRAPHICS | Automatically set by the system, in case of an HDGraphics graphics card. |
To enable a shader section call: AddDefine(GORILLA_GLSL_DEFINE_*)
To disable a shader section call: RemoveDefine(GORILLA_GLSL_DEFINE_*)
In most cases definitions need to be applied to vertex- and fragmentshader, because in both shaders functionality is available.
The DefaultMaterial defines the following sections by default:
Shader | Defines |
---|---|
VertexShader | GORILLA_GLSL_DEFINE_VERTEXSHADER, GORILLA_GLSL_DEFINE_CLIPPINGPLANE |
FragmentShader | GORILLA_GLSL_DEFINE_FRAGMENTSHADER, GORILLA_GLSL_DEFINE_CLIPPINGPLANE |
Globals
As already mentioned in the sections above, the default shader declares attributes, types, constants and variables automatically.
Those entities can be used inside the shader and can of course be modified.
In the following we list the available presets.
Attributes
Each vertex provides further information besides the position of it.
But depending on the mesh data given, not all of those properties may be available.
Nevertheless the shader defines a fixed set of attributes, even if they are not filled by the mesh data.
Name | Delphi-Type | GLSL-Type |
---|---|---|
a_Position | TPoint3D | vec3 |
a_Normal | TPoint3D | vec3 |
a_Tangent | TPoint3D | vec3 |
a_Binormal | TPoint3D | vec3 |
a_TexCoord0 | TPointF | vec2 |
a_TexCoord1 | TPointF | vec2 |
a_TexCoord2 | TPointF | vec2 |
a_TexCoord3 | TPointF | vec2 |
a_Color | TAlphaColorF | vec4 |
a_Color1 | TAlphaColorF | vec4 |
a_Color2 | TAlphaColorF | vec4 |
a_Color3 | TAlphaColorF | vec4 |
Uniform Buffer
In the current implementation of the default material a uniform buffer is used to submit settings as block to the shaders. While in previous implementations each variable was submitted separately, it became very inefficient on the large number of settings.
The default material allows to register multiple uniform buffers, but by default only the TGorillaShaderData structure is registered.
Notice: Structures inside of Delphi need a fixed alignment to OpenGL's std140 layout. You might have recognized a few “Reserved” fields. Those are marking a memory alignment inside of the structure.
Read more about OpenGL memory alignment here: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)
TGorillaShaderData
The TGorillaDefaultMaterial therefore registers a packed record as exchanging buffer.
Having a closer look at the structure below, it's showing further sub structures for lights, material, textures and render pass data. Those sub structures are automatically submitted to shaders too.
Notice: Modified since v1.1
TGorillaShaderData = packed record /// <summary> /// Defines the multiplied model, view and projection matrix. /// </summary> ModelViewProjectionMatrix : TMatrix3D; /// <summary> /// Defines the view / camera matrix. /// </summary> ViewMatrix : TMatrix3D; /// <summary> /// Defines the projection matrix. /// </summary> ProjectionMatrix : TMatrix3D; /// <summary> /// Provides information about start, current, previous and delta time /// X - Starting time /// Y - Current time /// Z - Previous time /// W - Delta time /// </summary> TimeInfo : TVector3D; /// <summary> /// The absolute camera position. /// </summary> EyePos : TVector3D; /// <summary> /// The camera view direction. /// </summary> ViewDir : TPoint3D; /// <summary> /// Defines the used shading model: lambert (default), phong, blinnphong, pbr, ... /// </summary> ShadingModel : FixedInt; {TGorillaShadingModel} /// <summary> /// Defines material properties, like diffuse, specular, ambient, emissive /// color or information about uniform textures. /// </summary> Material : Array[0..0] of TGorillaShaderMaterial; /// <summary> /// Defines activated light sources with their properties. /// </summary> Lights : Array[0..GORILLA_MATERIAL_DEFAULT_LIGHTS-1] of TGorillaShaderLight; /// <summary> /// Defines the number of lights used in the fix-sized lights array above, /// beginning at 0. /// </summary> LightsCount : FixedInt; /// <summary> /// Shader specific options. /// </summary> Options : FixedInt; /// <summary> /// Defines how much shading is applied to diffuse and specular color. /// </summary> ShadingIntensity : Single; /// <summary> /// Defines the brightness factor applied to the diffuse color. /// </summary> Brightness : Single; /// <summary> /// FogMode contains information about the algorithm to be used and if /// fog is enabled or not. /// </summary> FogMode : FixedInt; // $01 enabled, $02 - linear, $04 - exp, $08 - exp2 /// <summary> /// Defines the density of fog, used for exp and exp2 algorithm /// </summary> FogDensity : Single; // default = 0.001 /// <summary> /// Defines the eye-vertex distance where fog begins. /// </summary> FogStart : Single; // default = 100.0 /// <summary> /// Defines the eye-vertex distance where fog ends. /// </summary> FogEnd : Single; // default = 500.0 /// <summary> /// Define the fog color used for computation. /// </summary> FogColor : TAlphaColorF; // default = vec4(0, 0, 0, 1); /// <summary> /// Provides the camera frustum bounding box top-left-near corner. /// </summary> FrustumMin : TVector3D; /// <summary> /// Provides the camera frustum bounding box bottom-right-far corner. /// </summary> FrustumMax : TVector3D; /// <summary> /// Provides the top-left-near corner of the scenery bounds. /// </summary> SceneMin : TVector3D; /// <summary> /// Provides the bottom-right-far corner of the scenery bounds. /// </summary> SceneMax : TVector3D; /// <summary> /// Provides the top-left-near corner of the scenery bounds. /// </summary> BoundaryMin : TVector3D; /// <summary> /// Provides the bottom-right-far corner of the scenery bounds. /// </summary> BoundaryMax : TVector3D; /// <summary> /// Current viewport rendering size and near / far limitation. /// </summary> /// <remarks> /// <para> /// X - viewport width /// </para> /// <para> /// Y - viewport height /// </para> /// <para> /// Z - camera frustum near /// </para> /// <para> /// W - camera frustum far /// </para> /// </remarks> ViewSize : TVector3D; /// <summary> /// You can define a single clipping plane for discarding fragments below. /// </summary> ClippingPlane : TVector3D; /// <summary> /// Defines some pass data information, f.e. for raytracing, reflection and refraction. /// </summary> PassData : Array[0..0] of TGorillaShaderPassData; class function Create() : TGorillaShaderData; static; end;
NOTICE: Properties in uniform structures need to be accessed in OpenGL shaders by prefixing those witth an underscore (“_”), even for sub-properties of sub-structures.
You can access a top-level property inside of TShaderData in OpenGL shader, like:
vars.v_Position *= _ModelMatrix;
TGorillaShaderMaterial
The material property inside of the TGorillaShaderData struct defines a sub structure.
Notice: Even the material is defined as array, there is only a single material structure available.
Material : Array[0..0] of TGorillaShaderMaterial;
And the Delphi implementation of this structure is represented by the following packed record.
TGorillaShaderMaterial = packed record /// <summary> /// Emissive color value. /// </summary> Emissive : TAlphaColorF; /// <summary> /// Ambient color value. /// </summary> Ambient : TAlphaColorF; /// <summary> /// Diffuse color value. /// </summary> Diffuse : TAlphaColorF; /// <summary> /// Specular color value. /// </summary> Specular : TAlphaColorF; /// <summary> /// Clipping color value. R/G/B is the color value, A is the tolerance. /// </summary> Clipping : TAlphaColorF; /// <summary> /// Alpha cutoff value: (X > 0.01) == enabled, (Y == tolerated value) /// </summary> AlphaCutoff : TPointF; /// <summary> /// Tonemapping exposure value /// </summary> ToneMapExp : Single; Reserve : Single; /// <summary> /// Displacement factor to scale the influence of a displacement map. /// Default value is set to vec4(0.1, 0.1, 0.1, 1.0) /// </summary> /// <remarks> /// <para> /// (x, y, z) are the extension factors applied to the vertex normal. /// </para> /// <para> /// w - is the extension direction, while -1 means shrinking /// but +1 means model extrusion. /// </para> /// </remarks> Displacement : TVector3D; /// <summary> /// X - Current object opacity value. /// Y - reserved for future values /// Z - reserved for future values /// W - reserved for future values /// </summary> Features : TVector3D; /// <summary> /// Specular shining factor. If the value is <= 1.0, shading is deativated. /// </summary> Shininess : Single; /// <summary> /// Number of all textures /// </summary> Textures : FixedInt; /// <summary> /// The index of the first pool texture to find in texture settings: /// _Material[0]._Texture[_Material[0]._PoolTexturesOffset] /// </summary> PoolTexturesOffset : FixedInt; /// <summary> /// Number of total pool textures. /// </summary> PoolTextures : FixedInt; /// <summary> /// Fixed array of texture settings. /// </summary> Texture : TGorillaShaderTextures; class function Create() : TGorillaShaderMaterial; static; end;
You can access material settings in OpenGL shader, f.e. by:
LOCALS.Ambient = _Material[0]._Ambient;
TGorillaShaderTexture
Each material structure TGorillaShaderMaterial supports a set of max. 16 texture structures.
// type TGorillaShaderTextures = Array[0..GORILLA_SHADER_TEXTURES_MAX - 1] of TGorillaShaderTexture; Texture : TGorillaShaderTextures;
This allows to texture specific settings like transition values (for terrain material) or tile settings.
TGorillaShaderTexture = packed record /// <summary> /// for terrain textures we need information about height and transition /// between textures - those we put inside of a vec4 /// MinHeight, MaxHeight, LowTransition, HighTransition : Single /// </summary> Feature : TVector3D; /// <summary> /// Texture repeating value /// </summary> Tiling : TPointF; {$IFDEF GL_ES_VERSION_3_0} /// <summary> /// Due to memory alignment in gpu - we have to adjust memory here by /// some dummy bytes. /// </summary> Reserved1 : Array[0..7] of Byte; {$ENDIF} end;
You can access those settings in OpenGL shader, like:
LOCALS.Tiling.xy = _Material[0]._Texture[0]._Tiling.xy;
TGorillaShaderLight
The default material supports multiple light sources, which are submitted inside the TGorillaShaderData uniform structure as array of TGorillaShaderLight structure.
Lights : Array[0..GORILLA_MATERIAL_DEFAULT_LIGHTS-1] of TGorillaShaderLight;
The size of that array is platform dependent.
While on Windows platforms it is limited to 32, on Android it is limited to max. 8 lights.
TGorillaShaderLight = packed record LightType : FixedInt; {TLightType} {$IFDEF GL_ES_VERSION_3_0} // due to memory alignment in gpu - we have to adjust Reserved1 : Array[0..11] of Byte; {$ENDIF} Position : TVector3D; Direction : TVector3D; Ambient : TAlphaColorF; Diffuse : TAlphaColorF; Specular : TAlphaColorF; /// <summary> /// Light-Scattering settings are compressed into a single vec4 value, with /// the following structure: /// x=intensity, y=exposure, z=decay, w=density /// </summary> Scattering : TVector3D; SpotCutoff : Single; SpotCosCutoff : Single; SpotExponent : Single; ConstantAttenuation : Single; LinearAttenuation : Single; QuadraticAttenuation : Single; Intensity : Single; Enabled : FixedInt; constructor Create(ALight : TLightDescription); end;
You can access light settings in OpenGL shader, like:
LOCALS.BaseColor = _Light[0]._Diffuse;
TGorillaShaderPassData
In case you have run a render pass like reflections in a previous stage, some additional settings might be needed, like ReflectTextureMatrix or ReflectionPower.
So the PassData structure was introduced to group those settings in one place. Even if it is defined as an array, there is only a single pass data structure available.
PassData : Array[0..0] of TGorillaShaderPassData;
For more compressed data and less memory size, we merged various settings into this single structure. It might seem to be a little bit messed up, but it reduces memory usage in general.
Notice: We might refactor this structure in future, due to this confusing setup.
TGorillaShaderPassData = packed record ShadowMapMatrix : Array[0..7] of TMatrix3D; ReflectTextureMatrix : TMatrix3D; RefractTextureMatrix : TMatrix3D; ReflectionPower : Single; RefractionPower : Single; TranslucentDistance : Single; /// <summary> /// A factor that is applied on the normal vector in /// bump mapping / normal mapping / parallax occlusion mapping. /// </summary> NormalIntensity : Single; /// <summary> /// Define the number raytracing levels during parallax occlusion mapping. /// Allowed values are between 4 and 256. /// More levels meaning less performance due to more intense gpu usage. /// </summary> ParallaxLevels : FixedInt; /// <summary> /// Metallic value for controlling metalness of physically based rendering output. /// </summary> PBRMetallic : Single; /// <summary> /// Roughness value for controlling noise computation of physically based rendering output. /// </summary> PBRRoughness : Single; /// <summary> /// Ambient occlusion value for controlling ambient intensity of physically based rendering output. /// </summary> PBRAO : Single; end;
The PassData can be access in OpenGL shader like:
LOCALS.BaseColor.xyz *= _PassData[0]._ReflectionPower;
Varying values
We will not go into detail about shader stages. For further reading, please have a look at: https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview.
But simply said, the vertex shader is computed at first and already modifies vertex position or precomputes some values, we might use in our final fragment shader.
To supply those data, we need some kind of an exchange structure. This is called varying parameter, which is represented in GLSL by the _ _ INOUT _ _ struct, accessable by the “vars.” prefix.
Because the size of this structure is limited, we can only support a restricted number of properties.
It is not recommended to extend this structure, because exceeding those limits, might have unexpected effects!
The following list shows which properties are already defined:
Name | Delphi-Type | GLSL-Type | Description |
---|---|---|---|
vars.v_VertPos | TVector3D | vec4 | untransformed vertex position |
vars.v_TransfVertPos | TPoint3D | vec3 | transformed vertex position |
vars.v_WorldVertPos | TVector3D | vec4 | MVP transformed vertex position |
vars.v_TBN | TMatrix | mat3 | Array of three vectors for tangent, binormal and normal transformed by the model matrix. |
vars.v_TBN_UT | TMatrix | mat3 | Array of three vectors for tangent, binormal and normal untransformed (UT). |
vars.v_TexCoord0 | TPointF | vec2 | vertex texture coordinate for first texture |
vars.v_Color0 | TAlphaColorF | vec4 | first vertex color information (may be black if not provided) |
vars.v_ReflProjTextureCoords | TVector3D | vec4 | when reflection is active, these are the transformed coordinates for reflection texture |
vars.v_RefrProjTextureCoords | TVector3D | vec4 | when refraction is active, these are the transformed coordinates for refraction texture |
Default Textures
Name | GLSL-Type | Description | |
---|---|---|---|
_DisplacementMapTexture | VertexShader | sampler2D | If GORILLA_GLSL_DEFINE_DISPLACEMENT is activated. |
_Texture0 | FragmentShader | sampler2D / samplerCube / sampler3D (depending on the material used) | If GORILLA_GLSL_DEFINE_USE_TEXTURE is activated. |
_NormalMapTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_NORMALMAP is activated. |
_SpecularMapTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_USE_SPECULAR is activated. |
_EmissiveTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_USE_EMISSIVE is activated. |
_DepthTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_DEPTH is activated. |
_EnvironmentTexture | FragmentShader | samplerCube | If GORILLA_GLSL_DEFINE_ENVIRONMENT is activated. |
_ReflectionTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_REFLECTION is activated. |
_RefractionTexture | FragmentShader | sampler2D | If GORILLA_GLSL_DEFINE_REFRACTION is activated. |
Render Targets
The default material is using a multiple render target approach in fragment shaders.
This means it outputs values to multiple destination buffers.
Render Target | Defines | Description |
---|---|---|
o_Albedo | - | o_Albedo = LOCALS.SumColor |
o_Alpha | - | o_Alpha = LOCALS.Alpha |
o_Position | - | o_Position = vec4(LOCALS.TransfVertPos.xyz, LOCALS.FragDepth) |
o_Normals | - | o_Normals = LOCALS.Normal.xyz |
o_Components | GORILLA_GLSL_DEFINE_DEFERRED | o_Components = vec4(TexCoord0.xy, 1.0, 1.0) |
o_Ambient | GORILLA_GLSL_DEFINE_DEFERRED | o_Ambient = LOCALS.Ambient.xyz |
o_Emissive | GORILLA_GLSL_DEFINE_DEFERRED | o_Emissive = LOCALS.Emissive.xyz |
o_Specular | GORILLA_GLSL_DEFINE_DEFERRED | o_Specular = LOCALS.Specular.xyz |
Properties
Default material component provides a few easy-to-use properties for automatic defines settings. By using the following properties in your default material, you can setup the necessary defines, your shaders need for computation.
Property | Description |
---|---|
UseLighting | Enable or disable lighting shading model computation in shader |
UseTexturing | Enable or disable texturing functionality (using tex2D and tex2DProj in shader) |
UseTexture0 | Enable or disable “_Texture0” uniform variable and accessing Texture property. |
UseVertexColor | Enable or disable to use vertex color rendering. |
constructor TMyMaterial.Create(const ASource : TGorillaDefaultMaterialSource); begin inherited; UseTexturing := true; UseTexture0 := true; UseLighting := true; UseVertexColor := false; end;
NOTICE: Deactivating “UseTexture0” will remove “Texture” property availability! You can no longer use a Texture0 in your shader. Deactivate this property if you just want to use the diffuse color as base color.
Material Behaviours
Since v1.1 (Rev. 2724) we provide the material behaviour functionality.
Material Behaviours are a way to encapsulate shader functionality into building blocks. These building blocks can then be reused across multiple TGorillaDefaultMaterialSource components. This makes it easy to keep your shaders organized and maintainable.
NOTICE: Material behaviours are not compatible with embedded Firemonkey material sources, like TLightMaterialSource or TColorMaterialSourcce!
Apply a single material behaviour to multiple material sources, but the place to edit parameters of your behaviour stay centralized. This makes it very easy to manage complex shading behaviour.
Read more about Material Behaviour
Extending the default material
Here is a quick example for extending the default material to setup your own shader material to create a vertex color material.
- Gorilla.Material.VertexColor.pas
unit Gorilla.Material.VertexColor; interface uses System.SysUtils, System.Classes, FMX.Types, FMX.Types3D, FMX.Materials, FMX.MaterialSources, Gorilla.Material.Default; type TGorillaVertexColorMaterial = class(TGorillaDefaultMaterial) protected procedure DoCreateVertexShader(); override; procedure DoCreateFragmentShader(); override; public end; TGorillaVertexColorMaterialSource = class(TGorillaDefaultMaterialSource) protected function CreateMaterial() : TMaterial; override; public end; implementation uses Gorilla.Material.Default.GLSL; { TGorillaVertexColorMaterial } procedure TGorillaVertexColorMaterial.DoCreateVertexShader(); begin inherited; VertexShaderNode.AddDefine(GORILLA_GLSL_DEFINE_USE_COLOR); VertexShaderNode.RemoveDefine(GORILLA_GLSL_DEFINE_USE_TEXTURE); end; procedure TGorillaVertexColorMaterial.DoCreateFragmentShader(); begin inherited; FragmentShaderNode.AddDefine(GORILLA_GLSL_DEFINE_USE_COLOR); FragmentShaderNode.RemoveDefine(GORILLA_GLSL_DEFINE_USE_TEXTURE); end; { TGorillaVertexColorMaterialSource } function TGorillaVertexColorMaterialSource.CreateMaterial() : TMaterial; begin Result := TGorillaVertexColorMaterial.Create(Self); end; end.
Register a user-specific UniformBuffer
The default material allows to register more than one uniform buffer in shaders.
To create a new user-specific uniform buffer, you will need a representation of that structure in your default material class.
Notice: Structures inside of Delphi need a fixed alignment to OpenGL's std140 layout. Read more about OpenGL memory alignment here: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)
At first we define a packed record structure inside of Delphi.
PMyParams = ^TMyParams; TMyParams = packed record MyValue : Single; Reserved0 : Array[0..2] of Single; // taking care of memory alignment in TVector3D blocks. Point : TPoint3D; Reserved1 : Single; // taking care of memory alignment in TVector3D blocks. end;
The example above shows a structure declaration with memory alignment (Reserved0 + Reserved1). It should help to understand, how OpenGL aligns memory in std140 layout.
But in this exact case, we can optimize the record!
Because MyValue and Point fields fit into a single 4 * 4 Byte memory block of std140 alignment.
PMyParams = ^TMyParams; TMyParams = packed record Point : TPoint3D; MyValue : Single; end;
In GLSL the structure will appear as:
layout (binding=1, std140) uniform MyParams{ vec3 _Point; float _MyValue; };
In the next step, create a field inside of your material to store TMyParams.
TMyMaterial = class(TGorillaDefaultMaterial) protected FMyParams : TMyParams; /// override the update shader uniform buffers method to register our record in GPU. procedure DoUpdateShaderUniformBuffers(const Context: TContext3D; AProgram : TShaderProgram); override; end;
And prepare values for shader:
constructor TMyMaterial.Create(const ASource : TGorillaDefaultMaterialSource); begin inherited Create(ASource); FMyParams.MyValue := 0.25; FMyParams.Point := Point3D(1, -10, -1); end;
Afterwards we implement our register function:
procedure TMyMaterial.DoUpdateShaderUniformBuffers(const Context: TContext3D; AProgram : TShaderProgram); begin if not Assigned(AProgram) then Exit; inherited; /// let's register it in GPU shader. AProgram.RegisterUniformBuffer('MyParams', @FMyParams, SizeOf(FMyParams)); end;
In case we have dynamic values and need to modify the record values, we can use the DoApply() method. Because the memory is directly linked to the GPU, we can simply modify any value in our record and it will automatically be updated in shader.
procedure TGorillaWaterMaterial.DoApply(const Context: TContext3D); begin inherited; FMyParams.MyValue := Random(100) / 100; end;
Register a texture variable
Registering textures in shader is a little but different, but quite easy.
Because the default material manages a pool of textures, we only have to add a texture to it, and the component will do the rest for us.
Working further with our TMyMaterial, we need also a TMyMaterialSource to overwrite the protected DoCreateDefaultBitmaps() method.
type TMyMaterialSource = class(TGorillaMaterialSource) protected procedure DoCreateDefaultBitmaps(); override; end; [...] procedure TMyMaterialSource.DoCreateDefaultBitmaps(); var LEntry: TGorillaBitmapPoolEntry; begin /// /// adding new textures before calling "inherited" /// LEntry := DoAddOrUpdateBitmapToPool("MYTEXTURE0", TPixelFormatEx.RGBA8, TPixelFormatEx.RGBA, GORILLA_DEFAULT_TEXTURE_DATATYPE, false, true, true); /// in case you need to, modify the wrapping and filtering settings if Assigned(LEntry) then LEntry.SetExtendedProperties( TGorillaTextureWrap.Repeated, TGorillaTextureWrap.Repeated, TGorillaTextureWrap.Repeated, TTextureFilter.Linear, TTextureFilter.Linear); inherited; /// /// modifying default (existing) textures after calling "inherited". /// LEntry := Self.FindBitmapEntryByName(GORILLA_MATERIAL_TEXTURE_NORMALMAP); if Assigned(LEntry) then LEntry.SetExtendedProperties( TGorillaTextureWrap.Repeated, TGorillaTextureWrap.Repeated, TGorillaTextureWrap.Repeated, TTextureFilter.Linear, TTextureFilter.Linear); end;
Register a single variable
To add individual variables to the shaders you need to override the shader setup method.
Warning: This way adds a single variable to the shader and does not modify the uniform buffer!
Warning: Do not use this way to register texture variables! (Read more about in the section above.)
Shader | Function |
---|---|
VertexShader | DoSetupVertexShader(); |
FragmentShader | DoSetupFragmentShader(); |
Then simply use the AddVariables() routine of the shader source:
Shader | Function |
---|---|
VertexShader | FVSShaderSource.AddVariables([]); |
FragmentShader | FFSShaderSource.AddVariables([]); |
Because variables need an incrementing index on registration, you need to supply the shader source specific variable:
Shader | Field |
---|---|
VertexShader | FVSVarIndex |
FragmentShader | FFSVarIndex |
Here is a short example:
procedure TGorillaMyMaterial.DoSetupFragmentShader(); begin inherited; // use the fragment shader source to add a new variable FFSShaderSource.AddVariables( [ TContextShaderVariable.Create('MyPointF', TContextShaderVariableKind.Float2, FFSVarIndex, 0) ]); inc(FFSVarIndex, 1); end;
Declare the variable in GLSL source code
In the next step you need to add a new uniforms node in the specific shader. To access the node builder in the material, use one of the following variables:
Shader | Field |
---|---|
VertexShader | VertexShaderNode |
FragmentShader | FragmentShaderNode |
We add the new uniform node in the global uniform section of the node-tree to ensure, that is available anywhere in the code.
Take care to declare the variable name with the prefix “_”!
(Due to Firemonkey framework all uniform variables are declared by a leading underscore.)
procedure TGorillaMyMaterial.DoCreateFragmentShader(); var LUniform : TGorillaGLSLUniformsNode; begin inherited; LUniform := TGorillaGLSLUniformsNode.Create(FragmentShaderNode); LUniform.AddDeclaration('_MyPointF', TGorillaShaderNodeVariableType.stVec2); FragmentShaderNode.Globals.Uniforms.Add(LUniform); end;
You are allowed to add more than one variable in the uniforms node. There is no need to declare a single node for each variable.
Adding individual functions
To include a user specific function in shader source code, you need to declare a new node class inherited from TGorillaDeclarationNode. Then simply override the ToString() method and return your function declaration in shader language format.
In the DoCreateVertexShader() or DoCreateFragmentShader() function you create an instance of your declared node type and add it to the global function section of the node builder.
type TGorillaGLSLGetTextureColorFuncNode = class(TGorillaDeclarationNode) protected public function ToString() : String; override; end; function TGorillaGLSLGetTextureColorFuncNode.ToString() : String; begin Result := 'vec4 GetTextureColor(in sampler2D p_Texture, in vec2 p_TexCoord0){'#13#10 + ' return texture2D(p_Texture, p_TexCoord0);'#13#10 + '}'#13#10; end; procedure TGorillaMyMaterial.DoCreateFragmentShader(); var LNode : TGorillaNodeEntity; begin // FragmentShaderNode is the fragment shader node builder LNode := TGorillaGLSLGetTextureColorFuncNode.Create(FragmentShaderNode); FragmentShaderNode.Globals.Functions.Add(LNode); end;
Calling an individual functions
After we've declared our user specific function, we want to call it in the shader. For that we need a new node. Here the basic class TGorillaNodeEntity is quite perfect for the job.
Afterwards we have to attach the node to the node-builder again. The insert point depends on the job to do, of course.
In our example we've written a function that returns the current texture color. This is called base-color. The default shader declares a local variable “LOCALS.BaseColor” for that. If we set this, it will automatically be used in the color-light-summary.
The entry point for this node operation is the surface-shader node in the fragment shader: “FragmentShaderNode.SurfaceShader”.
Caution: The problem here is, that there is already a node (TGorillaGLSLGetBaseColorNode) that computes the “LOCALS.BaseColor” value. So we need to replace it with our node. But we have to ensure the correct position in node hierarchy for a valid shader code output.
type TGorillaGLSLCallGetTextureColorNode = class(TGorillaNodeEntity) protected public function ToString() : String; override; end; function TGorillaGLSLCallGetTextureColorNode.ToString() : String; begin Result := 'LOCALS.BaseColor = GetTextureColor(_Texture0, l_TexCoord0);'#13#10; end; procedure TGorillaMyMaterial.DoCreateFragmentShader(); var LNode : TGorillaNodeEntity; LOldIdx : Integer; begin [...] LNode := TGorillaGLSLCallGetTextureColorNode.Create(FragmentShaderNode); /// replace base color request LOldIdx := FragmentShaderNode.SurfaceShader.IndexOf(TGorillaGLSLGetBaseColorNode, ''); if (LOldIdx > -1) then begin /// remove old basecolor node FragmentShaderNode.SurfaceShader.Delete(LOldIdx); FragmentShaderNode.SurfaceShader.Insert(LOldIdx, LNode); end else FragmentShaderNode.SurfaceShader.Insert(0, LNode); end;
Changing Shaders at DesignTime
Since 0.8.4 it's possible to modify your default shader at designtime, by using published properties *VertexShader* or *SurfaceShader*.
Simply select your default material source, go to object inspector and open the text editor of the property. An empty GLSL function should be visible depending on the shader used.
For VertexShader:
void VertexShader(inout TLocals DATA){ }
For SurfaceShader (Fragment/Pixel-Shader):
void SurfaceShader(inout TLocals DATA){ }
You can then modify or extend the shader functionality. Please have a look a the section Shader-Variables for the available fields in TLocals structure.
NOTICE: Modifying the vertex position in vertex-shader all 3 fields “DATA.Position”, “DATA.TransfVertexPos” and “DATA.WorldViewProjVertPos” need to be modified. Otherwise their basis might not be equal which leads to unexpected effects!
In the following example we've created a wobbling effect in the vertex shader:
vec2 DELPHI_HASH2(vec2 n){ return fract(sin(n)*1399763.5453123); } float DELPHI_NOISE(vec2 st){ vec2 i = floor(st); vec2 f = fract(st); vec2 u = f * f * (3.0 - 2.0 * f); float a = dot(DELPHI_HASH2(i + vec2(0.0,0.0)), f - vec2(0.0, 0.0)); float b = dot(DELPHI_HASH2(i + vec2(1.0,0.0)), f - vec2(1.0, 0.0)); float c = dot(DELPHI_HASH2(i + vec2(0.0,1.0)), f - vec2(0.0, 1.0)); float d = dot(DELPHI_HASH2(i + vec2(1.0,1.0)), f - vec2(1.0, 1.0)); return mix(mix(a, b, u.x), mix(c, d, u.x), u.y); } vec3 DELPHI_WOBBLE(vec3 pos, float time, float intensity){ return vec3(pos.x, pos.y + (DELPHI_NOISE(vec2(pos.x, pos.z) + time) * intensity), pos.z); } void VertexShader(inout TLocals DATA){ DATA.Position.xyz = DELPHI_WOBBLE(DATA.Position.xyz, _TimeInfo.y, 0.5); DATA.TransfVertexPos = a_ModelMatrix * DATA.Position; DATA.WorldViewProjVertPos = _ModelViewProjectionMatrix * DATA.Position; }
Another example of a fragment shader to render an animated spritesheet with 3 x 3 rows and columns in a texture. The DefaultMaterial property “MeasureTime” needs to be set to TRUE.
Add a spritesheet texture to the material in the “Texture” property.
The shader discards fragments with a low alpha value for transparency. If you set <TControl3D>.Opaque to false, the alpha channel will be used for transparency, otherwise discarding a fragment is the correct way.
void SurfaceShader(inout TLocals DATA){ float SPRITE_COLUMNS = 3.0; float SPRITE_ROWS = 3.0; uint NUM_OF_SPRITES = 9; float t = _TimeInfo.y; uint sprite_idx = int(abs(sin(t) * (NUM_OF_SPRITES - 1))) % NUM_OF_SPRITES; vec2 pos = vec2(sprite_idx % int(SPRITE_COLUMNS), int(sprite_idx / SPRITE_COLUMNS)); DATA.BaseColor = tex2D(_Texture0, vec2((DATA.TexCoord0.x / SPRITE_COLUMNS) + pos.x * (1.0 / SPRITE_COLUMNS), (DATA.TexCoord0.y / SPRITE_ROWS) + pos.y * (1.0 / SPRITE_ROWS))); DATA.Alpha = DATA.BaseColor.a; if(DATA.Alpha < 0.04) discard; }
Read more about TLocals structure in VertexShader/SurfaceShader in section TGorillaGlobalsNode at TGorillaGLSLVertexShaderLocalsStructNode and TGorillaGLSLFragmentShaderLocalsStructNode.
To modify the “SurfaceShader” or “VertexShader” property at runtime, you have to work with a temporary TStrings component. Otherwise the shader code is not getting updated after modifying the user-code:
procedure TForm1.SetMyShaderCode(); var LStr : TStringList; begin /// Create a temporary stringlist component for our source code LStr := TStringList.Create(); try LStr.Text := 'void SurfaceShader(inout TLocals DATA){'#13#10 + /// make everything green ' DATA.BaseColor.xyz = vec3(0.0, 1.0, 0.0);'#13#10 + '}'; // Apply the temporary stringlist to the material // This will assign it to the internal stringlist and update the shader code // automatically. GorillaLambertMaterialSource1.SurfaceShader := LStr; finally FreeAndNil(LStr); end; end;
Adding new Textures at DesignTime
Besides the possibility of adding textures by code at runtime, you can also add new textures at designtime.
Move inside the object inspector to the specific material source (inherited from TGorillaDefaultMaterialSource).
Steps:
- Open the design editor of the “Bitmaps” property
- Add a new item
- Input a name for the texture
- Choose where the texture will be available: PixelShader or VertexShader
NOTICE: The name will be important to access the texture in your shader!
Due to Firemonkey implementation, you will access your texture with the prefix “_”. So for example, if you name your texture “MyTexture”, you will access it by “_MyTexture”.
When you choose the shader your texture will be available, you can access it also at design time in the corresponding property:
ShaderKind | Property |
---|---|
PixelShader | <TGorillaDefaultMaterialSource>.SurfaceShader |
VertexShader | <TGorillaDefaultMaterialSource>.VertexShader |
In the collection item you can configure further properties of your texture and you can of course load the image data itself.
NOTICE: Not all of the properties are fully restorable at runtime, because there are some automatisms which can't be overwritten by Gorilla3D. Use the “ProtectFormatSettings” property to at least try to protect those properties from implicit modification.
WARNING: When adding new texture always add them at the end of the collection, to not intercept default textures.
Availabled Properties
Property | Descr |
---|---|
Bitmap | Image data loadable from BMP, PNG or JPEG |
BorderColor | Defines a texture border color. |
DataType | Defines the data type of the internal texture format. See OpenGL specification for further information. |
Format | Defines the general texture format. See OpenGL specification for further information. |
IgnoreInArray | Internally arrays of textures are used for specific topics (shadows), where the name of the texture becomes obsolete. |
InternalFormat | Defines the internal texture format. See OpenGL specification for further information. |
IsDynamic | The texture will automatically retrieve its image data from a renderpass or other dynamic source. It's not recommended to use this with user specific textures. |
MagFilter | Defines the magnification texture filtering in combination with the MinFilter property. |
MinFilter | Defines the minification texture filtering in combination with the MagFilter property. |
Name | A name for the texture will always be needed! Access the texture in shaders with the prefix “_” |
ProtoectFormatSettings | TRUE/FALSE to protect the texture from implicit modification on re-init during runtime. |
ShaderKind | Defines where the texture will be available. In PixelShader or VertexShader. |
TextureKind | Kind of the texture: 2D, 3D, CubeMap, 2DArray (recommened: only 2D) |
TilingX | Tiling of the texture on x-axis (2D) |
TilingY | Tiling of the texture on y-axis (2D) |
WrapR | Texture wrapping on R-axis (for 3D textures) |
WrapS | Texture wrapping on S-axis |
WrapT | Texture wrapping on T-axis |