Hi there, I’m looking for a way to texture out a mesh while keeping all the lightings/shadows and all that good stuff rendered, is there any examples regarding StandardMaterial implementation in the shader?
I’d have to use several textures by height, I have a working example that uses default shader Material, but lack of knowledge stops me from getting further
Thanks
Hi @Newbie_Coder ,
What you are looking for is using shader chunks for authoring your custom shaders, so they work with PlayCanvas lighting/shadows.
Start with overriding the diffuse shader chunk.
Check the last two examples here on how to get started with that: Tutorials | Learn PlayCanvas
Hi there @Leonidas , I have checked all the shader examples, seems like last engine update hit them aswell, none of those are working, currently I’m playing with materials in the editor just to learn more and well I have a question, what makes material to change it’s rendering direction as shown in this pic:
The answer is UV channels
Would it be possible to force the render of material from bottom to top instead from side to side on the generated mesh?
Mesh has single UV channel
Okay, I’ve written a simple shader
Vert:
attribute vec3 aPosition;
attribute vec2 aUv0;
attribute vec3 aNormal;
varying float height;
uniform mat4 matrix_model;
uniform mat4 matrix_viewProjection;
varying vec3 vWorldPos;
varying vec4 vProjectedPos;
void main()
{
height = aPosition.y;
gl_Position = matrix_viewProjection * matrix_model * vec4( aPosition, 1.0 );
}
Frag
varying vec3 vWorldPos;
varying vec4 vProjectedPos;
varying float height;
uniform sampler2D sandTexture;
uniform sampler2D grassTexture;
void main(void)
{
vec2 uv2 = vWorldPos.xz;
vec4 sand = (step(0.0, height) - step(0.2, height)) * texture2D( sandTexture, uv2);
vec4 grass = (step(0.2, height) - step(3.0, height)) * texture2D( grassTexture, uv2);
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) + sand + grass;
}
// initialize code called once per entity
Water.prototype.initialize = function() {
var app = this.app;
var gd = app.graphicsDevice;
var shaderDefinition = {
attributes: {
aPosition: pc.SEMANTIC_POSITION,
aUv0: pc.SEMANTIC_TEXCOORD0,
aNormal: pc.SEMANTIC_NORMAL
},
vshader: this.vertexShader.resource,
fshader: "precision " + gd.precision + " float;\n\n" + this.fragmentShader.resource,
};
this.shader = new pc.Shader(gd, shaderDefinition);
var material = new pc.Material();
material.setShader(this.shader);
material.setParameter('sandTexture', this.sandTexture.resource);
material.setParameter('grassTexture', this.grassTexture.resource);
this.shaderLoaded = true;
this.material = material;
this.entity.render.meshInstances[0].material = this.material;
console.log(this.material);
};
I honestly have no idea how to pass shader chunks, after console logging this.material I can’t even see chunks property
Another problem: vec2 uv2 = vWorldPos.xz;
multiplying it should change the scale of textures but it doesn’t, why is that?
Thanks
Hi, I’m trying to convert a basic shader to Albedo one, I have several questions:
What is equivalent to a Position.y?
vWorldPos.xz = to?
Can Albedo be a Vec4?
Thanks
Newbie_Coder:
What is equivalent to a Position.y?
vWorldPos.xz = to?
If you are using shader chunks the following attribute is available in your code for the world position vPositionW
.
Newbie_Coder:
Can Albedo be a Vec4?
No albedo is a Vec3. But the question is why do you need a Vec4 for color? Do you need it for transparency?
If that’s the case you just need to override the opacity shader chunk and set the dAlpha value to your desired pixel transparency.
1 Like
I understand that shader chunks can be tricky to get started given it’s an unofficial and mostly undocumented feature.
I may spent some time tomorrow to setup a simple pixel shader using shader chunks on the default PlayCanvas terrain example, that may be of help to you.
1 Like
Newbie_Coder:
a Position.y
I think I am near the results but I cannot figure out replacement for aPosition.y (SEMANTIC_POSITION)
FRAGMENT varying height does not match any VERTEX varying
You can always override a vertex shader chunk (e.g. base or start) and pass your own varyings to your pixel shaders.
Frag
vec2 uv2 = vec2(vUv0.x * 15.0, vUv0.y * 15.0);
vec4 sand = (step(0.0, height) - step(0.1, height)) * texture2D( uTexTwo, uv2);
Vert
vec4 getPosition(){
vec2 uvCalc = vUv0;
vec3 pos = vertex_position;
vec3 height = vertex_position.y;
dPositionW = (matrix_model * vec4(vertex_position, 1.0)).xyz;
return matrix_viewProjection * matrix_model * vec4(pos, 1.0);
}
I’m using a varying float height
Vertex_position doesn’t work either while aPosition.y was the solution before Albedo
Or it won’t work that way?
What about a simple example with smoothstep that Mixes albedo with 2 textures instead/no height
Still trying to figure out how to pass aPosition to Albedo (I’m overwriting diffusePS), what about using a custom shader for texturing mesh (glFragColor) but also adding, albedo transperent texture over it
I’ll try and create an example later today on how to do that using shader chunks.
Hi @Newbie_Coder ,
Here is an example of a terrain smoothstep shader that mixes two textures based on the terrain elevation (height). The shader works by overriding the diffusePS shader chunk.
Posting both the full script code and an example public project, hope that helps!
var TerrainShader = pc.createScript('terrainShader');
TerrainShader.attributes.add('material', {
type: 'asset',
assetType: 'material'
});
TerrainShader.attributes.add('texture1', {
type: 'asset',
assetType: 'texture'
});
TerrainShader.attributes.add('texture2', {
type: 'asset',
assetType: 'texture'
});
TerrainShader.attributes.add('minElevation', {
type: 'number',
default: 0
});
TerrainShader.attributes.add('maxElevation', {
type: 'number',
default: 5
});
TerrainShader.attributes.add('stepElevation', {
type: 'number',
default: 0.5,
min: 0,
max: 1
});
TerrainShader.attributes.add('texBorder', {
type: 'number',
default: 0.03
});
// initialize code called once per entity
TerrainShader.prototype.initialize = function () {
const material = this.material.resource;
material.chunks.diffusePS = `
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float minElevation;
uniform float maxElevation;
uniform float stepElevation;
uniform float texBorder;
#ifdef MAPCOLOR
uniform vec3 material_diffuse;
#endif
void getAlbedo() {
dAlbedo = vec3(1.0);
#ifdef MAPCOLOR
dAlbedo *= material_diffuse.rgb;
#endif
#ifdef MAPTEXTURE
vec3 tex1 = gammaCorrectInput(texture2D(texture1, $UV, textureBias).rgb);
vec3 tex2 = gammaCorrectInput(texture2D(texture2, $UV, textureBias).rgb);
float elevationRange = maxElevation - minElevation;
float elevationFactor = clamp((vPositionW.y - minElevation) / elevationRange, 0.0, 1.0);
vec3 tex1Color = smoothstep(0.0, stepElevation + texBorder, elevationFactor) * tex1;
vec3 tex2Color = smoothstep(stepElevation - texBorder, 1.0, elevationFactor) * tex2;
dAlbedo = tex1Color + tex2Color;
#endif
#ifdef MAPVERTEX
dAlbedo *= gammaCorrectInput(saturate(vVertexColor.$VC));
#endif
}
`;
material.update();
this.setAttributes();
// --- events
this.on('attr', this.setAttributes);
};
TerrainShader.prototype.setAttributes = function () {
const material = this.material.resource;
material.setParameter('texture1', this.texture1.resource);
material.setParameter('texture2', this.texture2.resource);
material.setParameter('minElevation', this.minElevation);
material.setParameter('maxElevation', this.maxElevation);
material.setParameter('stepElevation', this.stepElevation);
material.setParameter('texBorder', this.texBorder);
};
https://playcanvas.com/editor/scene/1496498
4 Likes
Such a smart and clean code, many thanks! This is definitely going to be useful for the whole PlayCanvas community
1 Like
Pretty much understood the code except this part, what does vVertexColor and VC stand for?
That’s part of the default PlayCanvas diffuse shader chunk and it’s responsible for rendering the vertex colors of the model, if available and selected in the material.
It’s not really used in this sample, but I’ve kept it to keep the shader compatible.
export default /* glsl */`
#ifdef MAPCOLOR
uniform vec3 material_diffuse;
#endif
#ifdef MAPTEXTURE
uniform sampler2D texture_diffuseMap;
#endif
void getAlbedo() {
dAlbedo = vec3(1.0);
#ifdef MAPCOLOR
dAlbedo *= material_diffuse.rgb;
#endif
#ifdef MAPTEXTURE
vec3 albedoBase = gammaCorrectInput(texture2D(texture_diffuseMap, $UV, textureBias).$CH);
dAlbedo *= addAlbedoDetail(albedoBase);
#endif
This file has been truncated. show original
1 Like