I am trying to finish a ssao shader from a tutorial: http://www.nutty.ca/?page_id=352&link=ssao
I use multi-pass render in three.js.
There is a similar question in stackoverflow SSAO artefacts in Three, and I find we learned from the same tutorial.
I follow the tutorial in detail, I do not know which texture I render is totally wrong. Does anybody have some idea? thank you!
As following the tutorial,
I store the whole scene in a color tDiffusion texture:
,
store a view-space position to a tPosition texture:
,
store view-space linear depth to a tDepth texture:
,
the Shader are like this:
"custom_vsPosition" :{
uniforms:{
"cameraNear": { type: "f", value: 1.0 },
"cameraFar": { type: "f", value: 100.0 }
},
vertexShader:[
"varying vec4 mvPosition;", //View-space position
"void main() {",
// Calculate and include linear depth
" mvPosition = modelViewMatrix * vec4(position, 1.0);",
" gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader:[
"uniform float cameraNear;",
"uniform float cameraFar;",
"varying vec4 mvPosition;", //View-space position
"void main() {",
" float linearDepth = length(mvPosition) / (cameraFar - cameraNear);",
" linearDepth = clamp(linearDepth, 0.0, 1.0);",
" gl_FragColor = vec4(mvPosition.xyz, linearDepth);",
"}"
].join("\n")
},
store view-space normal vector to a tNormal texture:
,
"custom_vsNormal" :{
uniforms:{
},
vertexShader:[
"varying vec3 vNormal ;",
"void main(void) {",
" vNormal = normalMatrix * normal ;", //transforming the normal vector into view
//space and then scaling and biasing the vector components
" gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}"
].join("\n"),
fragmentShader:[
"varying vec3 vNormal ;", //Normal transform
"void main() {",
" gl_FragColor = vec4(normalize( vNormal ).xyz, 1.0);",
"}"
].join("\n")
},
And then based on the tDepth, tPosition and tNormal to calculate the ao.
However, the Ao seems wrong. Comparing my texture to the turtorial, I do not find where is wrong. From SSAO artefacts in Three, Maybe there where problem in Normal generating. But I really generate the view-space normal.
only Ao picture:
Ao with color scene:
SSAO shader from the tutorial:
"custom_NVD_SSAOShader":
{
uniforms: {
"tDiffuse": { type: "t", value: null }, // Render texture
"tPosition": { type: "t", value: null }, // View space position data
"tNormal": { type: "t", value: null }, // View space normal vectors
"tNoise": { type: "t", value: null },// Normalmap to randomize the sampling kernel
"size": { type: "v2", value: new THREE.Vector2( 512, 512 ) },
"occluderBias": { type: "f", value: 0.005 }, // Occluder bias to minimize self-occlusion.
"samplingRadius": { type: "f", value: 20 }, // Specifies the size of the sampling radius.
"cAttenuation": { type: "v3", value: new THREE.Vector3( 1, 1.5, 0 ) },
"onlyAo": { type: "f", value: 0.0 },
"lumInfluence": { type: "f", value: 0.9 }
// Ambient occlusion attenuation values. These parameters control the amount of AO calculated based on distance to the occluders. You need to play with them to find the right balance.
// .x = constant attenuation. This is useful for removing self occlusion. When
// set to zero or a low value, you will start to notice edges or wireframes
// being shown. Typically use a value between 1.0 and 3.0.
// .y = linear attenuation. This provides a linear distance falloff.
// .z = quadratic attenuation. Smoother falloff, but is not used in this shader.
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"#define sampleCount 16",
"#define patternSize 4.0",
"varying vec2 vUv;",
"uniform sampler2D tDiffuse;",
"uniform sampler2D tPosition;",
"uniform sampler2D tNormal;",
"uniform sampler2D tNoise;", //input random noise for random sampling
"uniform vec2 size;",
"uniform float occluderBias;",
"uniform float samplingRadius;",
"uniform vec3 cAttenuation;",
"uniform float onlyAo;",
"uniform float lumInfluence;",
"const vec3 onlyAOColor = vec3( 1.0, 0.7, 0.5 );",
"float SamplePixels (vec3 srcPosition, vec3 srcNormal, vec2 uv)",
"{",
// Get the 3D position of the destination pixel
" vec3 dstPosition = texture2D(tPosition, uv).xyz;",
// Calculate ambient occlusion amount between these two points
// It is simular to diffuse lighting. Objects directly above the fragment cast
// the hardest shadow and objects closer to the horizon have minimal effect.
" vec3 positionVec = dstPosition - srcPosition;",
" float intensity = max(dot(normalize(positionVec), srcNormal)- occluderBias, 0.0);",
" float dist = length(positionVec);",
" float attenuation = 1.0 / (cAttenuation.x + (cAttenuation.y * dist));",
" return intensity * attenuation;",
"}",
"vec2 texelSize = vec2(0.5 , 0.5);",
/*
// These are the Poisson Disk Samples
"vec2 getPoisson16(int i){;",
"vec2 poisson16;",
"if(i==0)",
" poisson16 = vec2( -0.94201624, -0.39906216 );",
"else if(i==1)",
" poisson16 = vec2( 0.94558609, -0.76890725 );",
"else if(i==2)",
" poisson16 = vec2( -0.094184101, -0.92938870 );",
"else if(i==3)",
" poisson16 = vec2( 0.34495938, 0.29387760 );",
"else if(i==4)",
" poisson16 = vec2( -0.91588581, 0.45771432 );",
"else if(i==5)",
" poisson16 = vec2( -0.81544232, -0.87912464 );",
"else if(i==6)",
" poisson16 = vec2( -0.38277543, 0.27676845 );",
"else if(i==7)",
" poisson16 = vec2( 0.97484398, 0.75648379 );",
"else if(i==8)",
" poisson16 = vec2( 0.44323325, -0.97511554 );",
"else if(i==9)",
" poisson16 = vec2( 0.53742981, -0.47373420 );",
"else if(i==10)",
" poisson16 = vec2( -0.26496911, -0.41893023 );",
"else if(i==11)",
" poisson16 = vec2( 0.79197514, 0.19090188 );",
"else if(i==12)",
" poisson16 = vec2( -0.24188840, 0.99706507 );",
"else if(i==13)",
" poisson16 = vec2( -0.81409955, 0.91437590 );",
"else if(i==14)",
" poisson16 = vec2( 0.19984126, 0.78641367 );",
"else if(i==15)",
" poisson16 = vec2( 0.14383161, -0.14100790 );",
"return poisson16;",
"}",
*/
"void main ()",
"{",
// Get position and normal vector for this fragment
" vec3 srcPosition = texture2D(tPosition, vUv).xyz;",
" float srcDepth = texture2D(tPosition, vUv).w;",
" vec3 srcNormal = texture2D(tNormal, vUv).xyz;",
/*
" float ao = 0.0;",
" float kernelRadius = samplingRadius * (1.0 - srcDepth);",
" for(int i =0; i < sampleCount; ++i)",
" {",
" ao += SamplePixels(srcPosition, srcNormal, vUv + getPoisson16(i) * kernelRadius);",
" }",
" ao /= 16.0;",
" ao = clamp(ao, 0.0, 1.0);",
" ao = 1.0 - ao;",
*/
" vec2 randVec = normalize(texture2D(tNoise, vUv).xy * 2.0 - 1.0);",
" float kernelRadius = samplingRadius * (1.0 - srcDepth);",
" vec2 kernel[4];",
" kernel[0] = vec2(0.0, 1.0);", // top",
" kernel[1] = vec2(1.0, 0.0);", // right
" kernel[2] = vec2(0.0, -1.0);", // bottom
" kernel[3] = vec2(-1.0, 0.0);", // left
" const float Sin45 = 0.707107;", // 45 degrees = sin(PI / 4)
" float ao = 0.0;",
" for (int i = 0; i < 4; ++i)",
" {",
" vec2 k1 = reflect(kernel[i], randVec);",
" vec2 k2 = vec2(k1.x * Sin45 - k1.y * Sin45,",
" k1.x * Sin45 + k1.y * Sin45);",
" k1 *= texelSize;",
" k2 *= texelSize;",
" ao += SamplePixels(srcPosition, srcNormal, vUv + k1 * kernelRadius);",
" ao += SamplePixels(srcPosition, srcNormal, vUv + k2 * kernelRadius * 0.75);",
" ao += SamplePixels(srcPosition, srcNormal, vUv + k1 * kernelRadius * 0.5);",
" ao += SamplePixels(srcPosition, srcNormal, vUv + k2 * kernelRadius * 0.25);",
" }",
" ao /= 16.0;",
" ao = clamp(ao, 0.0, 1.0);",
" ao = 1.0 - ao;",
" vec3 color = texture2D( tDiffuse, vUv ).rgb;",
"if ( onlyAo == 1.0 )",
" gl_FragColor.xyz = vec3( ao, ao, ao );",
"else if ( onlyAo == 2.0 )",
" gl_FragColor.xyz = vec3( srcNormal.x, srcNormal.y, srcNormal.z );",
"else if ( onlyAo == 3.0 )",
" gl_FragColor.xyz = vec3( srcDepth, srcDepth, srcDepth );",
"else if ( onlyAo == 4.0 )",
" gl_FragColor.xyz = vec3( srcPosition.x, srcPosition.y, srcPosition.z );",
"else if ( onlyAo == 5.0 )",
" gl_FragColor.xyz = color;",
"else",
"{",
" gl_FragColor.xyz = color * ao;",
"}",
"gl_FragColor.w = 1.0;",
"}"
].join("\n")
}
Here is the fragment shader code I am trying to use (in OpenGL ES 2.0, OpenGLES GLSL ES 1.00):
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"uniform float offset[] = float[]( 0.0000, 1.3846, 3.2307); \n"
"uniform float weight[] = float[]( 0.2270, 0.3162, 0.0702); \n"
"void main() \n"
"{ \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"} \n";
I am basically following the examples on this page, but I get ERROR:LEX/PARSE-1 (fragment shader, line 4) Syntax error.
If I try to declare them as const rather than uniform as suggested here (under Array Constructors), I get ERROR:CUSTOM-5 (fragment shader, line 4) Array cannot be const.
Obviously lines 4 and 5 will have the same problem. How do I get this to compile? What is the correct syntax?
I'd do it like this:
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"void main() \n"
"{
" vec3 offset = vec3(0.0000, 1.3846, 3.2307); \n"
" vec3 weight = vec3( 0.2270, 0.3162, 0.0702); \n" \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"}"
or this if you need it passed to the shader as a uniform:
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"uniform vec3 offset; \n"
"uniform vec3 weight; \n"
"void main() \n"
"{ \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"}"
or use uniform float[n] where n is the desired (constant) size in the second example if you need an array, or const float[n] in the first example if you need an array and you don't want to pass the value from your C++ code.
Turns out that this is just a limitation of GLES. It can't be done. An alternative method needs to be found.
setShaders
muOffsetHandle = getUniformLocation("offset");
muWeightHandle = getUniformLocation("weight");
useProgram
GLES20.glUniform1fv(muOffsetHandle, 1, mOffset, 0);
GLES20.glUniform1fv(muWeightHandle, 1, mWeight, 0);
vars
private int muOffsetHandle;
private int muWeightHandle;
protected float mOffset[] =new float[] {0.0f, 1.3f, 3.3f};
protected float mWeight[] =new float[] {0.2f, 0.3f, 0.3f};
FragmentShader
"uniform float offset[3];\n" +
"uniform float weight[3];\n" +
then trying to reach: weight[i]
I get this:
Shader log: 0:13: S0004: Member reference or swizzle attempted on non-structure and non-vector
line 13: tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy + vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];
(i = 0 to 2)
so my question: How to bypassing a float array to a uniform? float[3]
the code
protected static final String mFShader =
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform float uTime;\n" +
"uniform float uScreenWidth;\n" +
"uniform float uScreenHeight;\n" +
"uniform sampler2D uFrameBufferTexture;\n"+
"uniform float offset[3];\n" +
"uniform float weight[3];\n" +
"void main() {\n"+
" vec3 tc = vec3(1.0, 0.0, 0.0);\n"+
" if (vTextureCoord[0].x<(uTime-0.01)) {\n"+
" tc = texture2D(uFrameBufferTexture, vTextureCoord[0].xy).rgb * weight[0];\n"+
" for (int i=1; i<3; i++) {\n"+
" tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy + vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];\n"+
" tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy - vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];\n"+
" }\n"+
" }\n"+
" else if (vTextureCoord[0].x>=(uTime+0.01)) {\n"+
" tc = texture2D(uFrameBufferTexture, vTextureCoord[0].xy).rgb;\n"+
" }\n"+
" gl_FragColor = vec4(tc, 1.0);\n"+
"}\n";
The problem isn't actually with weight or offset. The compiler is complaining that you say vTextureCoord[0].xy, but you declared vTextureCoord as varying vec2 vTextureCoord;.
Either declare vTextureCoord as an array, or don't say [0].
You don't need to declare it as an array, but if you want you can simply use:
varying vec4 vTextureCoord[3]; // you can use anything you want between the brackets
Also, don't forget to modify the varying the the vertex shader too so that they will be identical.