GLSL For loop has "Invalid Condition" - glsl

Im trying to get a piece of GLSL code from shadertoy to function in WebGL.
Specifically this shadertoy by iq: https://www.shadertoy.com/view/Xds3zN
Ive implemented the correct uniform objects and it seems to work with most shadertoy shaders I convert, however, the one in the link above has several issues.
I solved everything except for a single error which states
ERROR line 381 For Invalid condition
The modified code I made is located here: https://pastebin.com/tgRhLNpA (Its too large to post in here)
The error is in the for loop condition within this block:
// raymarch primitives
vec2 tb = iBox( ro-vec3(0.0,0.4,-0.5), rd, vec3(2.5,0.41,3.0) );
if( tb.x<tb.y && tb.y>0.0 && tb.x<tmax)
{
//return vec2(tb.x,2.0);
tmin = max(tb.x,tmin);
tmax = min(tb.y,tmax);
float t = tmin;
for(int i=0; (i < 70) && (t < tmax); i++)
{
vec2 h = map( ro+rd*t );
if( abs(h.x)<(0.0001*t) )
{
res = vec2(t,h.y);
break;
}
t += h.x;
}
}
The original code IQ used was i<70 && t<tmax which errored, so I tried (i < 70) && (t < tmax) and I also tried (i < 70 && t < tmax) but both also had the same error..
As far as Im aware, Shadertoy/OpenGL and WebGL should share the same GLSL syntax features (iirc, shadertoy compiles GLSL into HLSL, Im not 100% sure), so Im confused as to why it would be invalid when I ran it with WebGL, but valid with Shadertoy

Related

How to íterate through an array of sampler2DShadow in GLSL 1.40 (Open GL 3.1)?

I want to iterate through an array of sampler2DShadow for a shadow map computation (multiple lights), and it looks like the sampler can be accessed only for a constant index. I only show the relevant code for clarity:
smooth in vec4 fShadowTexCoord[5];
uniform sampler2DShadow shadowMapTexSampler[5];
void main() {
for (uint i = 0u; i < 5u; i++) { // light 'i'
float shadowCoeff = 0.0f;
// this line does not work properly
vec2 scale = 2.0f / textureSize(shadowMapTexSampler[i], 0);
float bias = 0.006f;
for (int j = -1; j <= 1; j++)
for (int k = -1; k <= 1; k++)
shadowCoeff += texture(shadowMapTexSampler[i], vec3(fShadowTexCoord[i].xy + vec2(j, k) * scale, fShadowTexCoord[i].z - bias));
shadowCoeff /= 9.0f;
// ...........
}
}
The function 'texture' works as expected, but 'textureSize' function behaves like no texture is bound.
If I replace 'i' with a constant, it works fine, but I didn't found how to cast an 'uint' to a 'const uint' in GLSL.
sorry for the necro post, but this seems to be a limitation of GLSL prior to 4.60 or so?
I do something ugly like this:
#define PCF_LOOP(idx) \
else if (faceIdx == idx) \
{ \
for (int i=0; i<PCF_SAMPLES; i++) \
{ \
...
} \
}
if (false);
PCF_LOOP(0)
PCF_LOOP(1)
PCF_LOOP(2)
PCF_LOOP(3)
PCF_LOOP(4)
PCF_LOOP(5)
it's ugly and (probably) slow, but works even on ancient hw

Recursion in GLSL prohibited?

I ran into this error when trying to write the following recursive call. I have seen a lot of demos of implementations of recursive Ray tracing in GLSL so I assumed that GLSL supported recursion.
Is this not the case?
OpenGL is returning a compile time error message:
Error: Function trace(vec3, vec3, vec3, int) has static recursion
This is my function definition:
vec3 trace(vec3 origin, vec3 direction, vec3 illum, int order)
{
float dist;
int s_index = getSphereIntersect(origin, direction, dist);
//if light hit
float light_dist = 200;
for(int k = 0; k < L_COUNT;k++)
if(s_intersects(l_center[k], l_radius[k],
origin, direction,
light_dist))
if(light_dist < dist )
return l_color[k]; //light is pure color
if (s_index != -1)
{
illum = s_color[s_index];
for(int j = 0; j < L_COUNT; j++)
{
float ambient = 0.68;
float diffuse = 0.5;
vec3 poi = view + (direction * dist);
vec3 li_disp = normalize( poi - l_center[j]);
vec3 poi_norm = s_normal(s_center[s_index], s_radius[s_index], poi);
float shade= dot(li_disp, normalize(poi_norm));
if(shade < 0) shade = 0;
illum = illum*l_color[j]*ambient + diffuse * shade;
//test shadow ray onto objects, if shadow then 0
if(order > 0)
illum = trace(poi+.0001*poi_norm, poi_norm, illum, order-1);
}
}
else
illum = vec3(0,0,0);
return illum;
}
I assumed that GLSL supported recursion
No. GLSL doesn't support or better said allow recursive function calls.
GLSL does not. The GLSL memory model does not allow for recursive function calls. This allows GLSL to execute on hardware that simply doesn't allow for recursion. It allows GLSL to function when there is no ability to write arbitrarily to memory, which is true of most shader hardware (though it is becoming less true with time).
So, no recursion in GLSL. Of any kind.
– OpenGL Wiki – Core Language (GLSL)
and
Recursion is not allowed, not even statically. Static recursion is present if the static function-call graph of
a program contains cycles. This includes all potential function calls through variables declared as
subroutine uniform (described below). It is a compile-time or link-time error if a single compilation unit
(shader) contains either static recursion or the potential for recursion through subroutine variables.
– GLSL 4.5 Specification, Page 115

Very strange behaviour with sampler handling using OpenGL and GLSL

I have implemented cubemap shadow mapping successfully with just one point light.
To render this scene I use in the first render pass geometry shaders to dispatch the 6 frustrums. In the second render pass I use samplerCubeShadow in the fragment shader to computer the shadow factor.
I have OpenGL/GLSL version 4.40 with NVIDIA GeForce GTX 780M.
Here's a screenshot:
But now I want to implement multiple cubemap shadow mapping to render shadows using several point lights.
Here's some peace of code from my fragment shader:
[...]
/*
** Shadow Cube sampler array.
*/
uniform samplerCubeShadow ShadowCubeSampler[5]; //Max point light by scene = 5
[...]
float ConvertDistToClipSpace(vec3 lightDir_ws)
{
vec3 AbsVec = abs(lightDir_ws);
float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));
float NormZComp = (NearFar.y + NearFar.x)/(NearFar.y - NearFar.x)
- (2.0f * NearFar.y * NearFar.x)/(LocalZcomp * NearFar.y - NearFar.x);
return ((NormZComp + 1) * 0.5f);
}
float GetCubeShadowFactor(vec3 vertexPosition_ws, float shadowFactor, int idx)
{
vec3 lightToVertexDir_ws = vertexPosition_ws - LightPos_ws.xyz;
float LightToVertexClipDist = ConvertDistToClipSpace(lightToVertexDir_ws);
float LightToOccluderClipDist = texture(
ShadowCubeSampler[idx], vec4(lightToVertexDir_ws, LightToVertexClipDist));
if (LightToOccluderClipDist < LightToVertexClipDist)
{
shadowFactor = 0.0f;
}
return (shadowFactor);
}
void main(void)
{
[...]
for (int idx = 0; idx < 1; idx++) //Test first with 1 point light
{
float ShadowFactor = GetCubeShadowFactor(Position_ws.xyz, ShadowFactor, idx);
}
[...]
}
The problem is I have the error 1282 (INVALID_OPERATION). To resume the situation here, I want to display exactly the same scene like in the picture above with a SINGLE point light but this time using an array of samplerCubeShadow. What is amazing is if I replace the first parameter of the function 'texture' 'ShadowCubeSampler[idx]' by 'ShadowCubeSampler[0]' is works! However the value of 'idx' is always '0'. I tried the following code without success:
int toto = 0;
float LightToOccluderClipDist = texture(ShadowCubeSampler[toto], vec4(lightToVertexDir_ws, LightToVertexClipDist));
I already have the error 1282! The type of the index is the same (int)!
I have already use arrays of 'sampler2DShadow' or 'sampler2D' without problem.
So, Why it does not work correctly using 'samplerCubeShadow' and the solution 'ShadowCubeSampler[0]' works and not the others ?
PS: If I define an array of 2 and if I use 2 cubemaps so 2 point lights, it works. So, if I load a number of cubemaps inferior to the number specified in the fragment shader it fails!
I have no compilation error and no linkage error. Here's the code I use to check shader programs state:
void video::IEffectBase::Log(void) const
{
GLint errorLink = 0;
glGetProgramiv(this->m_Handle, GL_LINK_STATUS, &errorLink);
if (errorLink != GL_TRUE)
{
GLint sizeError = 0;
glGetProgramiv(this->m_Handle, GL_INFO_LOG_LENGTH, &sizeError);
char *erreur = new char[sizeError + 1];
glGetShaderInfoLog(this->m_Handle, sizeError, &sizeError, erreur);
erreur[sizeError] = '\0';
std::cerr << erreur << std::endl;
glDeleteProgram(this->m_Handle);
delete[] erreur;
}
}
And about the texture unit limits:
std::cout << GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS << std::endl;
std::cout << GL_MAX_TEXTURE_IMAGE_UNITS << std::endl;
$> 35660
$> 34930
If I use 'ShadowCubeSampler[0]', '0' written directly in the code I have the same display like the picture a the beginning of the my post without error. If I use 'ShadowCubeSampler[idx]' with idx = 0 I have the following display:
As you can see, all the geometry sharing this shader has not been rendered. However I don't have any linkage error. How can you explain that ? Is it possible the system unlink the shader program?
UPDATE
Let's suppose my array of samplerCubeShadow can contain 2 maximum samplers (uniform samplerCubeShadow tex_shadow2).
I noticed if I load just one point light, so one cubemap:
CASE 1
uniform samplerCubeShadow tex_shadow[1]; //MAX POINT LIGHT = 1
for (int i=0; i < 1; i++) {tex_shadow[i];} //OK
for (int i=0; i < 1; i++) {texture(tex_shadow[i], ...);} //OK
for (int i=0; i < 1; i++) {texture(tex_shadow[0], ...);} //OK
CASE 2
uniform samplerCubeShadow tex_shadow[2]; //MAX POINT LIGHT = 2
for (int i=0; i < 1; i++) {tex_shadow[i];} //NOT OK - 1282
for (int i=0; i < 1; i++) {texture(tex_shadow[i], ...);} //NOT OK - 1282
for (int i=0; i < 1; i++) {texture(tex_shadow[0], ...);} //OK
CASE 3
uniform samplerCubeShadow tex_shadow[2]; //MAX POINT LIGHT = 2
for (int i=0; i < 2; i++) {tex_shadow[i];} //OK
for (int i=0; i < 2; i++) {texture(tex_shadow[i], ...);} //OK
for (int i=0; i < 2; i++) {texture(tex_shadow[0], ...);} //OK
Conclusion: if the max number of sampler is equal to the number of sampler loaded, I can loop over the samplers contained in my array. If the number is inferior, it does not work! I can use a maximum of 32 texture units for each use of shader program. I have the same problem using the samplerCube keyword.
It's very strange because I don't have any problem using sampler2D or sampler2DShadow for spot light shadow computation.
I check with NSight where I put a break point in the fragment shader file and of course the break point is neaver reached. It's like the shader program is not linked but it's not the case.
Do you think it could be a problem concerning cubeMap samplers in general or the problem comes from the cubemap initialization ?
Does anyone can help me?
i have never use an array inside of glsl and infortuntly i dont have the equipments now to do so,
but have you tried using an unsigned int uint in glsl.
float GetCubeShadowFactor(vec3 vertexPosition_ws, float shadowFactor, uint idx) {
....
}
also note that you cannot use infinite samplers in you shaders.
OpenGL has, depending on the targeted version, special restrictions on arrays of opaque types (textures are one of them). Before OpenGL 4 looping over such arrays is not possible. You can check the details here: OpenGL Wiki - Data Types

GLSL for loop acting weird

I tried to implement something in glsl to do texture splatting, but the for loop is acting weird and gives different results for code that does exactly the same.
Code 1:
for(int i = 0; i < 5; ++i) {
if(i == 1) {
float fade = texture2D(alphaTextures[i], texCoord.st).r;
vec4 texCol = texture2D(textures[i], texCoord.ba);
texColor = mix(texColor, texCol, fade);
}
}
Code 2:
for(int i = 0; i < 6; ++i) {
if(i == 1) {
float fade = texture2D(alphaTextures[i], texCoord.st).r;
vec4 texCol = texture2D(textures[i], texCoord.ba);
texColor = mix(texColor, texCol, fade);
}
}
The if statement is just for testing purposes so that it should give the same result. The only difference is the loop condition. I really have no idea why only Code 1 gives the correct result. Here are two pictures:
Code1
Code2
The result should be like in picture 1.
According to this answer, you can't iterate over a sampler array. The index alphaTextures[i] is invalid, you can only use alphaTextures[1].
This changes in GLSL 4.00+ (OpenGL 4.0+), where you can have a variable index, but it cannot be from a shader input/derived value.
One reason could be that Graphic processors don't like branched texture fetches.
Try this instead:
for(int i = 0; i < 6; ++i) {
float fade = texture2D(alphaTextures[i], texCoord.st).r;
vec4 texCol = texture2D(textures[i], texCoord.ba);
if(i == 1) {
texColor = mix(texColor, texCol, fade);
}
}
(disclaimer) i am only guessing and this error is really weird.

Am I doing something wrong with this CG program?

I am using Ogre3D as the graphics engine.
I create a mesh manually which works fine, uvs are correct and are set to represent grid coordinates (for this example the grid is a 10 x 10)
I do nothing in the vertex program and have a very simple fragment program. I have included both programs plus the material file to explain.
My problem is, that even with filtering set to none the colours don't seem to come out the same as my original image (this is just a test image im using because I was having problems with creating the texture manually in ogre). It turns out that the problem is not my code in ogre but more likely something to do with either the material file or the fragment/vertex programs.
I have also included a screenshot of the output on the left and the original image on the right. The fragment shader also draws a simple grid over the top so I could make sure that uv coordinates were being passed across correctly. Which they seem to be.
Any insight would be much appreciated as I am really unsure what im doing wrong.
Material file:
// CG Vertex shader definition
vertex_program PlainTexture_VS cg
{
// Look in this source file for shader code
source GameObjStandard.cg
// Use this function for the vertex shader
entry_point main_plain_texture_vp
// Compile the shader to vs_1_1 format
profiles arbvp1
// This block saves us from manually setting parameters in code
default_params
{
// Ogre will put the worldviewproj into our 'worldViewProj' parameter for us.
param_named_auto worldViewProj worldviewproj_matrix
// Note that 'worldViewProj' is a parameter in the cg code.
}
}
// CG Pixel shader definition
fragment_program PlainTexture_PS cg
{
// Look in this source file for shader code
source GameObjStandard.cg
// Use this function for the pixel shader
entry_point main_plain_texture_fp
// Compile to ps_1_1 format
profiles arbfp1
}
material PlainTexture
{
// Material has one technique
technique
{
// This technique has one pass
pass
{
// Make this pass use the vertex shader defined above
vertex_program_ref PlainTexture_VS
{
}
// Make this pass use the pixel shader defined above
fragment_program_ref PlainTexture_PS
{
}
texture_unit 0
{
filtering none
// This pass will use this 2D texture as its input
texture test.png 2d
}
texture_unit 1
{
texture textureatlas.png 2d
tex_address_mode clamp
filtering none
}
}
}
}
CG File:
void main_plain_texture_vp(
// Vertex Inputs
float4 position : POSITION, // Vertex position in model space
float2 texCoord0 : TEXCOORD0, // Texture UV set 0
// Outputs
out float4 oPosition : POSITION, // Transformed vertex position
out float2 uv0 : TEXCOORD0, // UV0
// Model Level Inputs
uniform float4x4 worldViewProj)
{
// Calculate output position
oPosition = mul(worldViewProj, position);
// Simply copy the input vertex UV to the output
uv0 = texCoord0;
}
void main_plain_texture_fp(
// Pixel Inputs
float2 uv0 : TEXCOORD0, // UV interpolated for current pixel
// Outputs
out float4 color : COLOR, // Output color we want to write
// Model Level Inputs
uniform sampler2D Tex0: TEXUNIT0,
uniform sampler2D Tex1: TEXUNIT1) // Texture we're going to use
{
//get the index position by truncating the uv coordinates
float2 flooredIndexes = floor(uv0);
if((uv0.x > 0.9 && uv0.x < 1.1)
|| (uv0.x > 1.9 && uv0.x < 2.1)
|| (uv0.x > 2.9 && uv0.x < 3.1)
|| (uv0.x > 3.9 && uv0.x < 4.1)
|| (uv0.x > 4.9 && uv0.x < 5.1)
|| (uv0.x > 5.9 && uv0.x < 6.1)
|| (uv0.x > 6.9 && uv0.x < 7.1)
|| (uv0.x > 7.9 && uv0.x < 8.1)
|| (uv0.x > 8.9 && uv0.x < 9.1)) {
float4 color1 = {1.0,0,0,0};
color = color1;
} else if((uv0.y > 0.9 && uv0.y < 1.1)
|| (uv0.y > 1.9 && uv0.y < 2.1)
|| (uv0.y > 2.9 && uv0.y < 3.1)
|| (uv0.y > 3.9 && uv0.y < 4.1)
|| (uv0.y > 4.9 && uv0.y < 5.1)
|| (uv0.y > 5.9 && uv0.y < 6.1)
|| (uv0.y > 6.9 && uv0.y < 7.1)
|| (uv0.y > 7.9 && uv0.y < 8.1)
|| (uv0.y > 8.9 && uv0.y < 9.1)) {
float4 color1 = {1.0,0,0,0};
color = color1;
} else {
//get the colour of the index texture Tex0 at this floored coordinate
float4 indexColour = tex2D(Tex0, (1.0/10)*flooredIndexes);
color = indexColour;
}
}
Ok so its been a while since I found the solution to my problems unfortunately not been online so hope this helps anyone with similar issues.
When creating any texture you should always make textures a size in texels 2^n * 2^m where m and n are the width and height of the texture. This was my first mistake although I did not realise it at the time.
The reason I had not spotted this was because my main texture atlas was based on this principle and was a 1024 x 1024 texture. What I had not taken into account was the size of the texture I wasd creating as the texture index. Since my map was 10 x 10 I was creating a 10 x 10 texture for the indexes, this was I presume then being stretched somehow (not sure how it works in the backend) to be either 16 x 16 or 8 x 8, blending the texels together as it did it.
The first thing that gave me the clue was when I scaled my canvas in photoshop and found that the blended colours it was creating were the same as the ones I was getting on my ogre3d output.
Anyway moving on..
Once I had this figured out I was able to create the texture in Ogre and pass it across as follows
//Create index material
Ogre::TexturePtr indexTexture = Ogre::TextureManager::getSingleton().createManual("indexTexture","General",Ogre::TextureType::TEX_TYPE_2D, 16, 16, 0, Ogre::PixelFormat::PF_BYTE_BGRA, Ogre::TU_DEFAULT);
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = indexTexture->getBuffer();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data);
Ogre::uint8 counter = 0;
for (size_t j = 0; j < 16; j++) {
for(size_t i = 0; i < 16; i++)
{
if(i==8 || i==7) {
*pDest++ = 3; // B
*pDest++ = 0; // G
*pDest++ = 0; // R
*pDest++ = 0; // A
} else {
*pDest++ = 1; // B
*pDest++ = 0; // G
*pDest++ = 0; // R
*pDest++ = 0; // A
}
counter++;
}
}
pixelBuffer->unlock();
So now I have a texture I can use as an index with some values I added in for testing, these values eventually will be populated at runtime by clicking on the tile.
Now to pass this texture across I had to pass it to the correct technique and pass in my material, this was done as follows:
Ogre::MaterialPtr material = Ogre::MaterialPtr(Ogre::MaterialManager::getSingleton().getByName("PlainTexture"));
float mapSize = 16;
float tas = 2;
material->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("mapSize",mapSize);
material->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("tas",tas);
material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("indexTexture");
This passes two values too, mapSize being the size of the map itself in tiles (assuming its a square) and tas being the texture atlas size (number of different texture squares across the width of the atlas).
To allow my material to understand what I just passed in I needed to modify my material file slightly as follows:
// CG Pixel shader definition
fragment_program PlainTexture_PS cg
{
source GameObjStandard.cg
entry_point main_plain_texture_fp
profiles arbfp1
default_params
{
param_named tas float
param_named
}
}
And my pass was redefined slightly too
pass
{
// Make this pass use the vertex shader defined above
vertex_program_ref PlainTexture_VS
{
}
// Make this pass use the pixel shader defined above
fragment_program_ref PlainTexture_PS
{
}
texture_unit 0
{
filtering none
}
texture_unit 1
{
texture textureatlas.png 2d
tex_address_mode clamp
filtering anisotropic
}
}
I then rewrote the cg texture fragment program to take into account the changes I had made.
void main_plain_texture_fp(
float2 uv0 : TEXCOORD0, // UV interpolated for current pixel
out float4 color : COLOR, // Output color we want to write
uniform float tas,
uniform float mapSize,
// Model Level Inputs
uniform sampler2D Tex0: TEXUNIT0,
uniform sampler2D Tex1: TEXUNIT1)
{
//get the index position by truncating the uv coordinates
float2 flooredIndexes = floor(uv0);
//get the colour of the index texture Tex0 at this floored coordinate
float4 indexColour = tex2D(Tex0, ((1.0/mapSize) * flooredIndexes)+(0.5/mapSize));
//calculate the uv offset required for texture atlas range = 0 - 255
float indexValue = (255 * indexColour.b) + (255 * indexColour.g) + (255 * indexColour.r);
//float indexValue = (tas * tas) - indexValue0;
if(indexValue < tas*tas) {
float row = floor(indexValue/tas);
float col = frac(indexValue/tas) * tas;
float uvFraction = 1.0/tas;
float uBase = col * uvFraction;
float vBase = 1 - ((tas - row) * uvFraction);
float uOffset = frac(uv0.x)/tas;
float vOffset = (frac(uv0.y))/tas;
float uNew = uBase + uOffset;
float vNew = vBase + vOffset;
float2 uvNew = {uNew, vNew};
if(frac(uv0.x) > 0.99 || frac(uv0.x) < 0.01) {
float4 color1 = {1,1,1,0};
color = (0.2*color1) + (0.8*tex2D(Tex1,uvNew));
} else if(frac(uv0.y) > 0.99 || frac(uv0.y) < 0.01) {
float4 color1 = {1,1,1,0};
color = (0.2*color1) + (0.8*tex2D(Tex1,uvNew));
} else {
color = tex2D(Tex1,uvNew);
}
} else {
float4 color2 = {0.0,0,0,0};
color = color2;
}
}
This calculates the correct texel needed from the texture atlas, it also overlays a faint grid over the top by combining 80% texel color and 20% white.
If the texture atlas does not have the index of the colour specified by the index texture then it just outputs black (This is mainly so its very easy to spot.
Below is an example of the output using a 2 x 2 texture atlas.