Hey guys (@Deltakosh or @sebavan or @nasimiasl ) i got some custom shader code issues.
I am using a Advanced version of PBRCustomMateral i made for Unity Terrain Splatmaps
was working great for over a year. Then recently i tried running again and now i am getting undefined errors when trying to access uvOffset and normalW and TBN when trying to use my code to blend together the perturb normals from the #define CUSTOM_FRAGMENT_UPDATE_ALBEDO block. Now someone must have gone in and changed things again.
Can someone please tell me how to access the uvOffset and normalW and TBN from the custom code blocks ?
Do i have to add another #include replace pbrBlockXXXXXXXXX function like i had to do with:
fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);
Any help would be appreciated
1 Like
sebavan
December 17, 2020, 8:01pm
2
Adding @Evgeni_Popov who might have an idea of the new names
Actually, those string replace is not the final fix we provided. You should instead do what I have described in this post:
So, a PR has been merged:
With it, you can do this change in _buildCustomShader:
private _buildCustomShader(shaderName: string, uniforms: string[],
uniformBuffers: string[], samplers: string[], defines: BABYLON.PBRMaterialDefines,
attributes?: string[], options?: ICustomShaderNameResolveOptions) : string {
options!.processFinalCode = (type: string, code: string) => {
if (type === "vertex") {
return code;
}
const sci = new BABYLON.Shader…
With that in place, everything will (should) work as expected because the whole shader code (at least all the specific PBR code) will be inlined inside the main
function. If that still does not work, please provide the final fragment shader code that you generate which fails and we will have a more deeper look.
1 Like
Yo @Evgeni_Popov … Im sorry, i vaguely remember this. Am i supposed to string replace the code in the processFinalCode function… I dont get it… Do you have an example ? How about the PBRCustomMaterial… Dis you change this class to work with the processFinalCode stuff ?
If i am reading you right all i need is to stick that processFinalCode snippet at the top of my _buildCustomShader… Right ???
FYI… Here is the meat and potatoes of my custom shader builder:
private _buildCustomShader(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: BABYLON.PBRMaterialDefines, attributes?: string[], options?: BABYLON.ICustomShaderNameResolveOptions) : string {
options!.processFinalCode = (type: string, code: string) => {
if (type === "vertex") {
return code;
}
const sci = new BABYLON.ShaderCodeInliner(code);
sci.inlineToken = "#define pbr_inline";
sci.processCode();
return sci.code;
};
// Validate Property Defines
this._defines = defines;
const locals:any = this.locals.getDefines();
if (locals != null && this._defines != null) {
const keys:string[] = Object.keys(locals);
if (keys != null && keys.length > 0) {
const source:any = this._defines;
for (const key of keys) {
source[key] = locals[key];
}
this._defines.rebuild();
}
}
// Validate Property Uniforms
let index:number = 0;
if (this._uniforms != null && this._uniforms.length > 0) {
for (index = 0; index < this._uniforms.length; index++) {
const uniformName = this._uniforms[index];
if (uniforms.indexOf(uniformName) === -1) {
uniforms.push(uniformName);
}
}
}
// Validate Property Samplers
index = 0;
if (this._samplers != null && this._samplers.length > 0) {
for (index = 0; index < this._samplers.length; index++) {
const samplerName:string = this._samplers[index];
if (samplers.indexOf(samplerName) === -1) {
samplers.push(samplerName);
}
}
}
// Validate Property Attributes
index = 0;
if (this._attributes != null && this._attributes.length > 0) {
for (index = 0; index < this._attributes.length; index++) {
const attributeName:string = this._attributes[index];
if (attributes.indexOf(attributeName) === -1) {
attributes.push(attributeName);
}
}
}
// Validate Custom Attributes
index = 0;
const customAttributes:string[] = this.getCustomAttributes();
if (customAttributes != null && customAttributes.length > 0) {
for (index = 0; index < customAttributes.length; index++) {
const customAttributeName:string = customAttributes[index];
if (attributes.indexOf(customAttributeName) === -1) {
attributes.push(customAttributeName);
}
}
}
// Return Existing Shader Name
if (this._createdShaderName != null && this._createdShaderName !== "") {
return this._createdShaderName;
}
// Format Shader Program Name
let shaderProgram:string = this.getShaderName();
if (shaderProgram == null || shaderProgram === "") {
shaderProgram = shaderName;
}
if (shaderProgram == null || shaderProgram === "") {
shaderProgram = "pbr";
}
// Setup Shader After Bind Functions
const fn_afterBind = this._afterBind.bind(this);
this._afterBind = (m, e) => {
if (!e) { return; }
this._attachAfterBind(m, e);
try { fn_afterBind(m, e); } catch (e) { }
};
// Return Created Shader Name (With Created Setup)
this._createdShaderName = (this.enableShaderChunks === true) ? this._createShaderChunks(shaderProgram) : shaderProgram;
return this._createdShaderName;
}
private _createShaderChunks(shaderName: string): string {
let chunkName:string = this.getShaderChunk();
if (chunkName == null || chunkName === "") {
chunkName = shaderName;
}
// ..
BABYLON.UniversalShaderDefines.ShaderIndexer++;
const name: string = (chunkName + "Custom" + BABYLON.UniversalShaderDefines.ShaderIndexer).trim();
const vertex = BABYLON.Effect.ShadersStore[shaderName + "VertexShader"];
let fragment = BABYLON.Effect.ShadersStore[shaderName + "PixelShader"];
fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);
this.updateShaderChunks();
// ..
const vertexname = name + "VertexShader"
Effect.ShadersStore[vertexname] = vertex
.replace('#define CUSTOM_VERTEX_BEGIN', (this.materialShaderChunks.Vertex_Begin ? this.materialShaderChunks.Vertex_Begin : ""))
.replace('#define CUSTOM_VERTEX_DEFINITIONS', (this.materialShaderChunks.Vertex_Definitions ? this.materialShaderChunks.Vertex_Definitions : ""))
.replace('#define CUSTOM_VERTEX_MAIN_BEGIN', (this.materialShaderChunks.Vertex_MainBegin ? this.materialShaderChunks.Vertex_MainBegin : ""))
.replace('#define CUSTOM_VERTEX_UPDATE_POSITION', (this.materialShaderChunks.Vertex_Before_PositionUpdated ? this.materialShaderChunks.Vertex_Before_PositionUpdated : ""))
.replace('#define CUSTOM_VERTEX_UPDATE_NORMAL', (this.materialShaderChunks.Vertex_Before_NormalUpdated ? this.materialShaderChunks.Vertex_Before_NormalUpdated : ""))
.replace('#define CUSTOM_VERTEX_MAIN_END', (this.materialShaderChunks.Vertex_MainEnd ? this.materialShaderChunks.Vertex_MainEnd : ""));
if (this.materialShaderChunks.Vertex_After_WorldPosComputed) {
BABYLON.Effect.ShadersStore[vertexname] = BABYLON.Effect.ShadersStore[vertexname].replace('#define CUSTOM_VERTEX_UPDATE_WORLDPOS', this.materialShaderChunks.Vertex_After_WorldPosComputed);
}
// ..
const fragmentname = name + "PixelShader"
Effect.ShadersStore[fragmentname] = fragment
.replace('#define CUSTOM_FRAGMENT_BEGIN', (this.materialShaderChunks.Fragment_Begin ? this.materialShaderChunks.Fragment_Begin : ""))
.replace('#define CUSTOM_FRAGMENT_DEFINITIONS', (this.materialShaderChunks.Fragment_Definitions ? this.materialShaderChunks.Fragment_Definitions : ""))
.replace('#define CUSTOM_FRAGMENT_MAIN_BEGIN', (this.materialShaderChunks.Fragment_MainBegin ? this.materialShaderChunks.Fragment_MainBegin : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_ALBEDO', (this.materialShaderChunks.Fragment_Custom_Albedo ? this.materialShaderChunks.Fragment_Custom_Albedo : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.materialShaderChunks.Fragment_Custom_Alpha ? this.materialShaderChunks.Fragment_Custom_Alpha : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS', (this.materialShaderChunks.Fragment_MetallicRoughness ? this.materialShaderChunks.Fragment_MetallicRoughness : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE', (this.materialShaderChunks.Fragment_MicroSurface ? this.materialShaderChunks.Fragment_MicroSurface : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.materialShaderChunks.Fragment_Before_Lights ? this.materialShaderChunks.Fragment_Before_Lights : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.materialShaderChunks.Fragment_Before_Fog ? this.materialShaderChunks.Fragment_Before_Fog : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.materialShaderChunks.Fragment_Before_FragColor ? this.materialShaderChunks.Fragment_Before_FragColor : ""));
if (this.materialShaderChunks.Fragment_Before_Fog) {
BABYLON.Effect.ShadersStore[fragmentname] = BABYLON.Effect.ShadersStore[fragmentname].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.materialShaderChunks.Fragment_Before_Fog);
}
// ..
return name;
}
private _attachAfterBind(mesh:BABYLON.Mesh, effect:BABYLON.Effect):void {
let name: string;
const scene:BABYLON.Scene = this.getScene();
if (scene.texturesEnabled) {
for (name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture != null && texture.isReady && texture.isReady()) {
effect.setTexture(name, texture);
effect.setFloat2(name + "Infos", texture.coordinatesIndex, texture.level);
effect.setMatrix(name + "Matrix", texture.getTextureMatrix());
}
}
}
for (name in this._vectors4) {
effect.setVector4(name, this._vectors4[name]);
}
for (name in this._floats) {
effect.setFloat(name, this._floats[name]);
}
}
Yo @Evgeni_Popov … SO if i am now using the processFinalCode method… Do i still need to do this ???
fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);
sebavan
December 18, 2020, 12:02pm
7
@Evgeni_Popov is off until Monday so please be patient on this one @MackeyK24
Here’s an example using the feature:
This one is a little more involved because there’s no simple way to inject some code just after the alpha texture has been sampled.
You can use a feature that allows to tamper with the shader source code just before it is sent to the GPU to replace the alpha sampling code by your own (lines 51-64):
https://www.babylonjs-playground.com/#KWVY94#10
You can also write your own shader with ShaderMaterial but it’s even more involved.
You could also write a node material in the NME, using for eg uv…
In your code, you don’t need to do any replace like in the example but instead you need to inline the code by doing:
const sci = new BABYLON.ShaderCodeInliner(code);
sci.inlineToken = "#define pbr_inline";
sci.processCode();
return sci.code;
MackeyK24:
Yo @Evgeni_Popov … SO if i am now using the processFinalCode method… Do i still need to do this ???
fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);
No, you don’t need to do this anymore once you use processFinalCode
.
1 Like