Metal - the scene rendered upside down - glsl

I am trying to convert this glsl into my Metal app for learning purposes. It can render successfully. However, There are 2 issues:
the scene is upside down.
I use mouse to rotate the camera. When I move my mouse, the scene rotate to totally unseen angle.
I believe the 2 issues is related, How can I resolve this issues? Below are my Metal code:
#include <metal_stdlib>
using namespace metal;
constant const float gtime = 0.0;//<-- stop the animation for now.
constant const float pi = 3.141592653589793;
float sdPlane( float3 p) {
return p.y + 0.4;
}
float sdSphere( float3 p, float r) {
return length(p) - r;
}
float sdCapsule( float3 p, float3 a, float3 b, float r ) {
float3 pa = p - a, ba = b - a;
float h = clamp( dot(pa, ba) / dot(ba , ba), 0.0, 1.0 );
return length( pa - ba * h ) - r;
}
float motor(float _min, float _max, float time) {
float t = 0.5 + 0.5 * sin(time);
return mix(_min, _max, t);
}
float3 rotate_from_origin(float3 origin, float3 target, float r, float angle) {
return float3(
origin.x + r * cos(angle),
origin.y + r * sin(angle),
target.z
);
}
float3 preserve(float3 p0, float3 p1, float len) {
float3 v = p1 - p0;
float3 u = normalize(v);
return p0 + len * u;
}
float smin( float a, float b, float k ) {
float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
return mix( b, a, h ) - k*h*(1.0-h);
}
float2 smin2( float2 a, float2 b, float k ) {
float h = clamp( 0.5+0.5*(b.x-a.x)/k, 0.0, 1.0 );
return mix( b, a, h ) - k*h*(1.0-h);
}
float2 map( float3 p) {
float t = gtime * 2.0;
float cx = 0.2;
float cz = 0.1;
float3 p0 = float3(-cx, 0.0, 0.0);
float3 p1 = float3(-cx, -0.2, -cz);
float3 p2 = float3(-cx, -0.4, -cz);
float3 p3 = float3(-cx, 0.2, cz);
float3 p4 = float3(-cx, -0.4, cz);
float3 p5 = float3(cx, 0.0, 0.0);
float3 p6 = float3(cx, -0.2, -cz);
float3 p7 = float3(cx, -0.4, -cz);
float3 p8 = float3(cx, 0.2, cz);
float3 p9 = float3(cx, -0.4, cz);
float3 p10 = float3(0.0, 0.0, 0.0);
float3 p11 = float3(cx, -0.2, 0.0);
float angle0 = 0.0;
float angle1 = 0.0;
p0.y = -motor(-0.05, 0.05, t * 4.0);
angle0 = -motor(pi * 0.15, pi * 0.65, t * 2.0 - pi * 0.5);
angle1 = -motor(pi * 0.15, pi * 0.65, t * 2.0 + pi * 0.5);
p1 = rotate_from_origin(p0, p1, 0.2, pi-angle0);
p3 = rotate_from_origin(p0, p3, 0.2, pi-angle1);
angle0 += -motor(0.0, pi * 0.5, t * 2.0 + pi);
angle1 += -motor(0.0, pi * 0.5, t * 2.0 + pi + pi);
p2 = rotate_from_origin(p1, p2, 0.2, pi-angle0*0.6);
p4 = rotate_from_origin(p3, p4, 0.2, pi-angle1*0.6);
p5.y = -motor(-0.05, 0.05, t * 4.0);
angle0 = -motor(pi * 0.15, pi * 0.65, t * 2.0 - pi * 0.5);
angle1 = -motor(pi * 0.15, pi * 0.65, t * 2.0 + pi * 0.5);
p6 = rotate_from_origin(p5, p6, 0.2, angle0);
p8 = rotate_from_origin(p5, p8, 0.2, angle1);
angle0 += -motor(0.0, pi * 0.5, t * 2.0 + pi);
angle1 += -motor(0.0, pi * 0.5, t * 2.0 + pi + pi);
p7 = rotate_from_origin(p6, p7, 0.2, angle0*0.6);
p9 = rotate_from_origin(p8, p9, 0.2, angle1*0.6);
p10.y = -motor(-0.02, 0.02, t * 4.0 - pi * 0.5);
p11 = preserve(p5, p11, -0.25);
float w = 0.05;
float2 dd = float2(sdPlane(p - float3(0.0, -0.05, 0.0)), 1.0);
float2 d = float2(10.0);
d = smin2(d, float2(sdCapsule(p, p0, p1, w), 30.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p1, p2, w), 30.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p0, p3, w), 40.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p3, p4, w), 40.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p5, p6, w), 30.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p6, p7, w), 30.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p5, p8, w), 40.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p8, p9, w), 40.0), 0.001);
d = smin2(d, float2(sdCapsule(p, p0, p10, w + 0.0025 * sin(p.x * pi * 60.0)), 90.0), 0.1);
d = smin2(d, float2(sdCapsule(p, p10, p5, w), 90.0), 0.1 + 0.8*(0.5 + 0.5 * sin(gtime * 0.2)));
d = smin2(d, float2(sdCapsule(p, p5, p11, w), 90.0), 0.15);
d = smin2(d, dd, 0.01);
return d;
}
float3 calcNormal( float3 p) {
float2 e = float2(-1.0, 1.0) * 0.001;
float3 nor = normalize(
e.xyy * map(p + e.xyy).x +
e.yxy * map(p + e.yxy).x +
e.yyx * map(p + e.yyx).x +
e.xxx * map(p + e.xxx).x
);
return nor;
}
float2 castRay( float3 ro, float3 rd, float maxt) {
float precis = 0.001;
float h = precis * 2.0;
float t = 0.0;
float m = -1.0;
for(int i = 0; i < 60; i++) {
if(abs(h) < precis || t > maxt) continue;
float2 res = map(ro + rd * t);
h = res.x;
t += h;
m = res.y;
}
if(t > maxt) m = -1.0;
return float2(t, m);
}
float softshadow( float3 ro, float3 rd, float mint, float maxt, float k) {
float sh = 1.0;
float t = mint;
float h = 0.0;
for(int i = 0; i < 30; i++) {
if(t > maxt) continue;
h = map(ro + rd * t).x;
sh = min(sh, k * h / t);
t += h;
}
return sh;
}
float3 render( float3 ro, float3 rd) {
float3 col = float3(1.0);
float2 res = castRay(ro, rd, 20.0);
float t = res.x;
float m = res.y;
col = 0.45 + 0.3*sin(float3(0.05,0.08,0.10)*(m-1.0)+gtime);
if(abs(m - 1.0) < 0.01) col = float3(0.5);
float3 pos = ro + rd * t;
float3 nor = calcNormal(pos);
float3 lig = normalize(float3(-0.4, 0.7, 0.5));
float dif = clamp(dot(lig, nor), 0.0, 1.0);
float spe = pow(clamp(dot(reflect(rd, nor), lig), 0.0, 1.0), 64.0);
float fre = 1.0 - dot(-rd, nor);
float sh = softshadow(pos, lig, 0.02, 20.0, 7.0);
col = 1.0*col * (dif + spe + fre * 0.5) * (0.5 + sh * 0.5);
return col;
}
kernel void compute(texture2d<float, access::write> output [[texture(0)]],
constant float &time [[buffer(1)]],
constant float &mouseX [[buffer(2)]],
constant float &mouseY [[buffer(3)]],
uint2 gid [[thread_position_in_grid]]) {
int width = output.get_width();
int height = output.get_height();
float2 uv = float2(gid) / float2(width, height);
float2 p = uv * 2.0 - 1.0;
p.x *= width / height;
float2 ms = 2.0 * float2(mouseX,mouseY) - 1.0;
float3 ro = float3(ms.x * 2.0, 2.0 - ms.y, 1.5);
float3 ta = float3(0.0, 0.0, 0.0);
float3 cw = normalize(ta - ro);
float3 cp = float3(0.0, 1.0, 0.0);
float3 cu = normalize(cross(cw, cp));
float3 cv = normalize(cross(cu, cw));
float3 rd = normalize(p.x * cu + p.y * cv + 2.5 * cw);
float3 col = render(ro, rd);
output.write(float4(col, 1.), gid);
}

Based on Marius's comment, I found out the solution. I just invert the y value by adding a - then everything become normal again:
float2 ms = 2.0 * normalize(float2(mouseX,-mouseY)) - 1.0;//<-- make mouseY negative

Related

DX12) Trying to Implement Volumetric Scattering for multiple Spot Light, but It's not going well

(This Image is What I want to implement)
I am attempting Post Processing using Compute Shader to implement Light Shaft for multiple Spot Lights in the DX12 framework.
The first thing I tried was the method at the following link:https://gitlab.com/tomasoh/100_procent_more_volume/-/blob/master/shaders/volumetric.frag
It's a very complicated and hard-to-understand kind of shader, but it's basically built on the premise of using multiple lights, so it's a kind of example for the purpose.
However, since the game I'm making has 32 light source limitations, considering that excessive amount of Frame Drop will occur in the part of calculating Visibility by making Shadow Map for all light sources, I decided to implement Visibility as 1.0 Constant and did not get the desired result. (Of course it's a result.)
Down below is how I did this thing:
#include "lighting.hlsl"
Texture2D<float4> inputTexture : register(t0);
Texture2D<float> depthTexture : register(t1);
RWTexture2D<float4> outputTexture : register(u0);
#define PI 3.141592653589793238f
cbuffer VolumetricCB : register(b1)
{
float absorptionTau : packoffset(c0);
float3 absorptionColor : packoffset(c0.y);
int scatteringSamples : packoffset(c1.x);
float scatteringTau : packoffset(c1.y);
float scatteringZFar : packoffset(c1.z);
float3 scatteringColor : packoffset(c2);
matrix gInvProj : packoffset(c3);
matrix gInvView : packoffset(c7);
float3 gCameraPos : packoffset(c11);
Light gLights[NUM_LIGHTS] : packoffset(c12);
}
float random(float2 co)
{
return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453123);
}
float3 PixelWorldPos(float depthValue, int2 pixel)
{
uint width, height;
inputTexture.GetDimensions(width, height);
float2 fPixel = float2(pixel.x, pixel.y);
float x = (fPixel.x / width * 2) - 1;
float y = (fPixel.y / height * (-2)) + 1;
float z = depthValue;
float4 ndcCoords = float4(x, y, z, 1.0f);
float4 p = mul(ndcCoords, gInvProj);
p /= p.w;
float4 worldCoords = mul(p, gInvView);
return worldCoords.xyz;
}
float3 absorptionTransmittance(float dist)
{
return absorptionColor * exp(-dist * (absorptionTau + scatteringTau));
}
float phaseFunction(float3 inDir, float3 outDir)
{
float cosAngle = dot(inDir, outDir) / (length(inDir) * length(outDir));
float x = (1.0 + cosAngle) / 2.0;
float x2 = x * x;
float x4 = x2 * x2;
float x8 = x4 * x4;
float x16 = x8 * x8;
float x32 = x16 * x16;
float nom = 0.5 + 16.5 * x32;
float factor = 1.0 / (4.0 * PI);
return nom * factor;
}
float3 volumetricScattering(float3 worldPosition, Light light)
{
float3 result = float3(0.0, 0.0, 0.0);
float3 camToFrag = worldPosition - gCameraPos;
if (length(camToFrag) > scatteringZFar)
{
camToFrag = normalize(camToFrag) * scatteringZFar;
}
float3 deltaStep = camToFrag / (scatteringSamples + 1);
float3 fragToCamNorm = normalize(gCameraPos - worldPosition);
float3 x = gCameraPos;
float rand = random(worldPosition.xy + worldPosition.z);
x += (deltaStep * rand);
for (int i = 0; i < scatteringSamples; ++i)
{
float visibility = 1.0;
float3 lightToX = x - light.Position;
float lightDist = length(lightToX);
float omega = 4 * PI * lightDist * lightDist;
float3 Lin = absorptionTransmittance(lightDist) * visibility * light.Diffuse * light.SpotPower / omega;
float3 Li = Lin * scatteringTau * scatteringColor * phaseFunction(normalize(lightToX), fragToCamNorm);
result += Li * absorptionTransmittance(distance(x, gCameraPos)) * length(deltaStep);
x += deltaStep;
}
return result;
}
[numthreads(32, 32, 1)]
void CS(uint3 dispatchID : SV_DispatchThreadID)
{
int2 pixel = int2(dispatchID.x, dispatchID.y);
float4 volumetricColor = float4(0.0, 0.0, 0.0, 1.0);
float depthValue = depthTexture[pixel].r;
float3 worldPosition = PixelWorldPos(depthValue, pixel);
float fragCamDist = distance(worldPosition, gCameraPos);
for (int i = 0; i < NUM_LIGHTS; ++i)
{
if (gLights[i].Type == SPOT_LIGHT && gLights[i].FalloffEnd > length(gLights[i].Position - worldPosition))
volumetricColor += float4(volumetricScattering(worldPosition, gLights[i]), 0.0);
}
outputTexture[pixel] = volumetricColor + inputTexture[pixel];
}
(AbsorptionTau = -0.061f, ScatteringTau = 0.059f)
All these Codes for that Tiny Spot...
The second method was shown in Chapter 13 of GPU GEM3.
It was a method of drawing only Light Source on a separate Render Target, processing the Render Target using Post Processing Shder to create light scattering, and then merging it with a back buffer. (At least that's how I understand it.)
However, this method was designed only for one very strong light, and to fix it, I modified the code as below, but it didn't work well.
[numthreads(32, 32, 1)]
void CS(uint3 dispatchID : SV_DispatchThreadID)
{
uint2 pixel = dispatchID.xy;
uint width, height;
inputTexture.GetDimensions(width, height);
float4 result = inputTexture[pixel];
for (int i = 0; i < NUM_LIGHTS; ++i)
{
if(gLights[i].Type == SPOT_LIGHT)
{
float2 texCoord = float2(pixel.x / width, pixel.y / height);
float2 deltaTexCoord = (texCoord - mul(mul(float4(gLights[i].Position, 1.0f), gView), gProj).xy);
deltaTexCoord *= 1.0f / NUM_SAMPLES * Density;
float3 color = inputTexture[pixel].rgb;
float illuminationDecay = 1.0f;
for (int j = 0; j < NUM_SAMPLES; j++)
{
texCoord -= deltaTexCoord;
uint2 modifiedPixel = uint2(texCoord.x * width, texCoord.y * height);
float3 sample = inputTexture[modifiedPixel].rgb;
sample *= illuminationDecay * Weight;
color += sample;
illuminationDecay *= Decay;
}
result += float4(color * Exposure, 1);
}
}
outputTexture[pixel] = result;
}
this just 'Blur' these light source map, and surely it's not what I wanted.
Is there a similar kind of example to the implementation that I want, or is there a simpler way to do this? I've spent a week on this issue, but I haven't achieved much.
edit :
I did it! but there's some error about direction of light volume.
[numthreads(32, 32, 1)]
void CS(uint3 dispatchID : SV_DispatchThreadID)
{
float4 result = { 0.0f, 0.0f, 0.0f, 0.0f };
uint2 pixel = dispatchID.xy;
uint width, height;
inputTexture.GetDimensions(width, height);
float2 texCoord = (float2(pixel) + 0.5f) / float2(width, height);
float depth = depthTexture[pixel].r;
float3 screenPos = GetPositionVS(texCoord, depth);
float3 rayEnd = float3(0.0f, 0.0f, 0.0f);
const uint sampleCount = 16;
const float stepSize = length(screenPos - rayEnd) / sampleCount;
// Perform ray marching to integrate light volume along view ray:
[loop]
for (uint i = 0; i < NUM_LIGHTS; ++i)
{
[branch]
if (gLights[i].Type == SPOT_LIGHT)
{
float3 V = float3(0.0f, 0.0f, 0.0f) - screenPos;
float cameraDistance = length(V);
V /= cameraDistance;
float marchedDistance = 0;
float accumulation = 0;
float3 P = screenPos + V * stepSize * dither(pixel.xy);
for (uint j = 0; j < sampleCount; ++j)
{
float3 L = mul(float4(gLights[i].Position, 1.0f), gView).xyz - P;
const float dist2 = dot(L, L);
const float dist = sqrt(dist2);
L /= dist;
//float3 viewDir = mul(float4(gLights[i].Direction, 1.0f), gView).xyz;
float3 viewDir = gLights[i].Direction;
float SpotFactor = dot(L, normalize(-viewDir));
float spotCutOff = gLights[i].outerCosine;
[branch]
if (SpotFactor > spotCutOff)
{
float attenuation = DoAttenuation(dist, gLights[i].Range);
float conAtt = saturate((SpotFactor - gLights[i].outerCosine) / (gLights[i].innerCosine - gLights[i].outerCosine));
conAtt *= conAtt;
attenuation *= conAtt;
attenuation *= ExponentialFog(cameraDistance - marchedDistance);
accumulation += attenuation;
}
marchedDistance += stepSize;
P = P + V * stepSize;
}
accumulation /= sampleCount;
result += max(0, float4(accumulation * gLights[i].Color * gLights[i].VolumetricStrength, 1));
}
}
outputTexture[pixel] = inputTexture[pixel] + result;
}
this is my compute shader, but when I doesn't multiply view matrix to direction, it goes wrong like this :
as you can see, street lamp's volume direction is good, but vehicle's headlight's volume direction is different from it's spot light direction.
and when I multiply view matrix to direction :
head lights gone wrong AND street lamp goes wrong too.
I still finding where's wrong in my cpu codes, but I haven't find anything.
this might be helpful. here's my shader code about spot lighting.
float CalcAttenuation(float d, float falloffStart, float falloffEnd)
{
return saturate((falloffEnd - d) / (falloffEnd - falloffStart));
}
float3 BlinnPhongModelLighting(float3 lightDiff, float3 lightVec, float3 normal, float3 view, Material mat)
{
const float m = mat.Exponent;
const float f = ((mat.IOR - 1) * (mat.IOR - 1)) / ((mat.IOR + 1) * (mat.IOR + 1));
const float3 fresnel0 = float3(f, f, f);
float3 halfVec = normalize(view + lightVec);
float roughness = (m + 8.0f) * pow(saturate(dot(halfVec, normal)), m) / 8.0f;
float3 fresnel = CalcReflectPercent(fresnel0, halfVec, lightVec);
float3 specular = fresnel * roughness;
specular = specular / (specular + 1.0f);
return (mat.Diffuse.rgb + specular * mat.Specular) * lightDiff;
}
float3 ComputeSpotLight(Light light, Material mat, float3 pos, float3 normal, float3 view)
{
float3 result = float3(0.0f, 0.0f, 0.0f);
bool bCompute = true;
float3 lightVec = light.Position - pos;
float d = length(lightVec);
if (d > light.FalloffEnd)
bCompute = false;
if (bCompute)
{
lightVec /= d;
float ndotl = max(dot(lightVec, normal), 0.0f);
float3 lightDiffuse = light.Diffuse * ndotl;
float att = CalcAttenuation(d, light.FalloffStart, light.FalloffEnd);
lightDiffuse *= att;
float spotFactor = pow(max(dot(-lightVec, light.Direction), 0.0f), light.SpotPower);
lightDiffuse *= spotFactor;
result = BlinnPhongModelLighting(lightDiffuse, lightVec, normal, view, mat);
}
return result;
}

Implementing RayPicking in a Fragment Shader

I am having trouble implementing RayPicking in a Fragment-Shader, I understand I must start from my mouse coordinates, but I am not sure what to multiply my origin with.
I have tried creating a 3 component vector, with x and y as my mouse coordinates divided by my resolution, and in z I have tried using my p(for point in space, calculated as rayOrigin + rayDirection * t) with no luck.
Here is a Shadertoy that tries what I am looking for.
float ray( vec3 ro, vec3 rd, out float d )
{
float t = 0.0; d = 0.0;
for( int i = 0; i < STEPS; ++i )
{
vec3 p = ro + rd * t;
d = map( p );
if( d < EPS || t > FAR ) break;
t += d;
}
return t;
}
vec3 shad( vec3 ro, vec3 rd, vec2 uv )
{
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
float x = ( 2.0 * iMouse.x ) / iResolution.x - 1.0;
float y = 1.0 - ( 2.0 * iMouse.y ) / iResolution.y;
float z = 1.0;
vec3 p = ro + rd * t;
vec3 n = nor( p );
vec3 lig = ( vec3( x, -y, z ) );
lig += ro + rd;
lig = normalize( lig );
vec3 ref = reflect( rd, n );
float amb = 0.5 + 0.5 * n.y;
float dif = max( 0.0, dot( n, lig ) );
float spe = pow( clamp( dot( ref, lig ), 0.0, 1.0 ), 16.0 );
vec3 col = vec3( 0 );
col += 0.1 * amb;
col += 0.2 * dif;
col += spe;
return col;
}
I expect to get a light that moves as if I was shooting a ray from my mouse coordinates to the SDF.
This is the correct code:
// Our sphere-tracing algorithm.
float ray( vec3 ro, vec3 rd, out float d )
{
float t = 0.0; d = 0.0;
for( int i = 0; i < STEPS; ++i )
{
vec3 p = ro + rd * t;
d = map( p );
if( d < EPS || t > FAR ) break;
t += d;
}
return t;
}
// Here we compute all our lighting calculations.
vec3 shad( vec3 ro, vec3 rd, vec2 uv )
{
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
vec3 p = ro + rd * t;
vec3 n = nor( p );
// The values of the variable lig are not random they are in the same position as our rayOrigin for our sphere tracing algo, that goes in main's body.
vec3 lig = ( vec3( 0, 0, 2 ) );
// Here is where we "shoot" our ray from the mouse position. Our ray's origin.
vec2 uvl = ( -iResolution.xy + 2.0 * iMouse.xy ) / iResolution.y;
// This is our ray's direction.
vec3 lir = normalize( vec3( uvl, -1 ) );
// Here we get our SDF(dO) and our incrementing value(tO).
float dO = 0.0, tO = ray( lig, lir, dO );
// Now we update our vector with the direction and incrementing steps.
lig += lir * tO;
// We must normalize lights as they are just a direction, the magnitude screws the lighting calculations.
lig = normalize( lig );
vec3 ref = reflect( rd, n );
float amb = 0.5 + 0.5 * n.y;
float dif = max( 0.0, dot( n, lig ) );
float spe = pow( clamp( dot( ref, lig ), 0.0, 1.0 ), 16.0 );
vec3 col = vec3( 0 );
col += 0.1 * amb;
col += 0.2 * dif;
col += spe;
return col;
}
// Last step, here we create the origin and direction of our rays that we shoot against the SDF.
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
//Normalize the coordinates.
vec2 uv = ( -iResolution.xy + 2.0 * fragCoord.xy ) / iResolution.y;
// This is our ray's origin. We must use the same values for our lig's origin.
vec3 ro = vec3( 0, 0, 2 );
// This is our ray's direction.
vec3 rd = normalize( vec3( uv, -1 ) );
// Our SDF(d) and our incrementing steps(t), we only need our SDF(d) to bail the shading calculations according to our epsilon(EPS).
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
vec3 col = d < EPS ? shad( ro, rd, uv ) : vec3( 0 );
fragColor = vec4( col, 1 );
}

Point light Dual-paraboloid VSM in deferred rendering

I've been following this tutorial to implement my variance shadow mapping feature for point light in deferred rendering.
I'm using GLSL 3.3, left-handed coordinate system. Here is what I've been doing:
I render the scene to dual-paraboloid maps, storing depth and depth * depth.
Result:
Above image contains front and back maps. The point light is at the center of scene, you can see where it glows yellow the most.
Then I set up a full-screen shader pass.
I do this by transforming the tutorial code from FX to GLSL.
Author's .fx code:
float4 TexturePS(float3 normalW : TEXCOORD0, float2 tex0 : TEXCOORD1, float3 pos : TEXCOORD2) : COLOR
{
float4 texColor = tex2D(TexS, tex0 * TexScale);
pos = mul(float4(pos, 1.0f), LightView);
float L = length(pos);
float3 P0 = pos / L;
float alpha = .5f + pos.z / LightAttenuation;
P0.z = P0.z + 1;
P0.x = P0.x / P0.z;
P0.y = P0.y / P0.z;
P0.z = L / LightAttenuation;
P0.x = .5f * P0.x + .5f;
P0.y = -.5f * P0.y + .5f;
float3 P1 = pos / L;
P1.z = 1 - P1.z;
P1.x = P1.x / P1.z;
P1.y = P1.y / P1.z;
P1.z = L / LightAttenuation;
P1.x = .5f * P1.x + .5f;
P1.y = -.5f * P1.y + .5f;
float depth;
float mydepth;
float2 moments;
if(alpha >= 0.5f)
{
moments = tex2D(ShadowFrontS, P0.xy).xy;
depth = moments.x;
mydepth = P0.z;
}
else
{
moments = tex2D(ShadowBackS, P1.xy).xy;
depth = moments.x;
mydepth = P1.z;
}
float lit_factor = (mydepth <= moments[0]);
float E_x2 = moments.y;
float Ex_2 = moments.x * moments.x;
float variance = min(max(E_x2 - Ex_2, 0.0) + SHADOW_EPSILON, 1.0);
float m_d = (moments.x - mydepth);
float p = variance / (variance + m_d * m_d); //Chebychev's inequality
texColor.xyz *= max(lit_factor, p + .2f);
return texColor;
}
My translated GLSL code:
void main() {
vec3 in_vertex = texture(scenePosTexture, texCoord).xyz; // get 3D vertex from 2D screen coordinate
vec4 vert = lightViewMat * vec4(in_vertex, 1); // project vertex to point light space (view from light position, look target is -Z)
float L = length(vert.xyz);
float distance = length(lightPos - in_vertex);
float denom = distance / lightRad + 1;
float attenuation = 1.0 / (denom * denom);
// to determine which paraboloid map to use
float alpha = vert.z / attenuation + 0.5f;
vec3 P0 = vert.xyz / L;
P0.z = P0.z + 1;
P0.x = P0.x / P0.z;
P0.y = P0.y / P0.z;
P0.z = L / attenuation;
P0.x = .5f * P0.x + .5f;
P0.y = -.5f * P0.y + .5f;
vec3 P1 = vert.xyz / L;
P1.z = 1 - P1.z;
P1.x = P1.x / P1.z;
P1.y = P1.y / P1.z;
P1.z = L / attenuation;
P1.x = .5f * P1.x + .5f;
P1.y = -.5f * P1.y + .5f;
// Variance shadow mapping
float depth;
float mydepth;
vec2 moments;
if(alpha >= 0.5f)
{
moments = texture(shadowMapFrontTexture, P0.xy).xy;
depth = moments.x;
mydepth = P0.z;
}
else
{
moments = texture(shadowMapBackTexture, P1.xy).xy;
depth = moments.x;
mydepth = P1.z;
}
// Original .fx code is: float lit_factor = (mydepth <= moments[0]);
// I'm not sure my translated code belew is correct
float lit_factor = 0;
if (mydepth <= moments.x)
lit_factor = mydepth;
else
lit_factor = moments.x;
float E_x2 = moments.y;
float Ex_2 = moments.x * moments.x;
float variance = min(max(E_x2 - Ex_2, 0.0) + SHADOW_EPSILON, 1.0);
float m_d = (moments.x - mydepth);
float p = variance / (variance + m_d * m_d); //Chebychev's inequality
fragColor = texture(sceneTexture, texCoord).rgb; // sample original color
fragColor.rgb *= max(lit_factor, p + .2f);
}
Render result
Right now I'm clueless about where I'm gonna touch to render the shadow correctly. Could someone point it out for me?
Some friend of mine pointed out that the Y component is flipped, that's why shadow looked like up-side down. After adding minus to P0 and P1's Y, it starts to show quite reasonable shadow:
But another problem is the location of shadow is wrong.
Why do you duplicate the paraboloid projection computation ?
You compute it on 'vert', then 'P0' and 'P1', shouldn't you do it only on 'P0' and 'P1' ? (The original code doesn't do this thing on 'pos').
EDIT:
Your lit_factor is wrong, it should be either 0.0 or 1.0.
You could use the step() GLSL intrinsic, in this way :
float lit_factor = step(mydepth, moments[0]);

Customizing Page Curl Shader in OpenGL

I am currently designing a fragment shader that shall display a page curl effect as a transition between two images. So I looked it up on the Internet and found this code that works as I want:
varying vec2 texCoord;
uniform sampler2D sourceTex;
uniform sampler2D targetTex;
uniform float time; // Ranges from 0.0 to 1.0
const float MIN_AMOUNT = -0.16;
const float MAX_AMOUNT = 1.3;
float amount = time * (MAX_AMOUNT - MIN_AMOUNT) + MIN_AMOUNT;
const float PI = 3.141592653589793;
const float scale = 512.0;
const float sharpness = 3.0;
float cylinderCenter = amount;
// 360 degrees * amount
float cylinderAngle = 2.0 * PI * amount;
const float cylinderRadius = 1.0 / PI / 2.0;
vec3 hitPoint(float hitAngle, float yc, vec3 point, mat3 rrotation) {
float hitPoint = hitAngle / (2.0 * PI);
point.y = hitPoint;
return rrotation * point;
}
vec4 antiAlias(vec4 color1, vec4 color2, float distance) {
distance *= scale;
if (distance < 0.0) return color2;
if (distance > 2.0) return color1;
float dd = pow(1.0 - distance / 2.0, sharpness);
return ((color2 - color1) * dd) + color1;
}
float distanceToEdge(vec3 point) {
float dx = abs(point.x > 0.5 ? 1.0 - point.x : point.x);
float dy = abs(point.y > 0.5 ? 1.0 - point.y : point.y);
if (point.x < 0.0) dx = -point.x;
if (point.x > 1.0) dx = point.x - 1.0;
if (point.y < 0.0) dy = -point.y;
if (point.y > 1.0) dy = point.y - 1.0;
if ((point.x < 0.0 || point.x > 1.0) && (point.y < 0.0 || point.y > 1.0)) return sqrt(dx * dx + dy * dy);
return min(dx, dy);
}
vec4 seeThrough(float yc, vec2 p, mat3 rotation, mat3 rrotation) {
float hitAngle = PI - (acos(yc / cylinderRadius) - cylinderAngle);
vec3 point = hitPoint(hitAngle, yc, rotation * vec3(p, 1.0), rrotation);
if (yc <= 0.0 && (point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0)) {
return texture2D(targetTex, texCoord);
}
if (yc > 0.0) return texture2D(sourceTex, p);
vec4 color = texture2D(sourceTex, point.xy);
vec4 tcolor = vec4(0.0);
return antiAlias(color, tcolor, distanceToEdge(point));
}
vec4 seeThroughWithShadow(float yc, vec2 p, vec3 point, mat3 rotation, mat3 rrotation) {
float shadow = distanceToEdge(point) * 30.0;
shadow = (1.0 - shadow) / 3.0;
if (shadow < 0.0) shadow = 0.0;
else shadow *= amount;
vec4 shadowColor = seeThrough(yc, p, rotation, rrotation);
shadowColor.r -= shadow;
shadowColor.g -= shadow;
shadowColor.b -= shadow;
return shadowColor;
}
vec4 backside(float yc, vec3 point) {
vec4 color = texture2D(sourceTex, point.xy);
float gray = (color.r + color.b + color.g) / 15.0;
gray += (8.0 / 10.0) * (pow(1.0 - abs(yc / cylinderRadius), 2.0 / 10.0) / 2.0 + (5.0 / 10.0));
color.rgb = vec3(gray);
return color;
}
vec4 behindSurface(float yc, vec3 point, mat3 rrotation) {
float shado = (1.0 - ((-cylinderRadius - yc) / amount * 7.0)) / 6.0;
shado *= 1.0 - abs(point.x - 0.5);
yc = (-cylinderRadius - cylinderRadius - yc);
float hitAngle = (acos(yc / cylinderRadius) + cylinderAngle) - PI;
point = hitPoint(hitAngle, yc, point, rrotation);
if (yc < 0.0 && point.x >= 0.0 && point.y >= 0.0 && point.x <= 1.0 && point.y <= 1.0 && (hitAngle < PI || amount > 0.5)){
shado = 1.0 - (sqrt(pow(point.x - 0.5, 2.0) + pow(point.y - 0.5, 2.0)) / (71.0 / 100.0));
shado *= pow(-yc / cylinderRadius, 3.0);
shado *= 0.5;
} else
shado = 0.0;
return vec4(texture2D(targetTex, texCoord).rgb - shado, 1.0);
}
void main(void) {
const float angle = 30.0 * PI / 180.0;
float c = cos(-angle);
float s = sin(-angle);
mat3 rotation = mat3(
c, s, 0,
-s, c, 0,
0.12, 0.258, 1
);
c = cos(angle);
s = sin(angle);
mat3 rrotation = mat3(
c, s, 0,
-s, c, 0,
0.15, -0.5, 1
);
vec3 point = rotation * vec3(texCoord, 1.0);
float yc = point.y - cylinderCenter;
if (yc < -cylinderRadius) {
// Behind surface
gl_FragColor = behindSurface(yc, point, rrotation);
return;
}
if (yc > cylinderRadius) {
// Flat surface
gl_FragColor = texture2D(sourceTex, texCoord);
return;
}
float hitAngle = (acos(yc / cylinderRadius) + cylinderAngle) - PI;
float hitAngleMod = mod(hitAngle, 2.0 * PI);
if ((hitAngleMod > PI && amount < 0.5) || (hitAngleMod > PI/2.0 && amount < 0.0)) {
gl_FragColor = seeThrough(yc, texCoord, rotation, rrotation);
return;
}
point = hitPoint(hitAngle, yc, point, rrotation);
if (point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0) {
gl_FragColor = seeThroughWithShadow(yc, texCoord, point, rotation, rrotation);
return;
}
vec4 color = backside(yc, point);
vec4 otherColor;
if (yc < 0.0) {
float shado = 1.0 - (sqrt(pow(point.x - 0.5, 2.0) + pow(point.y - 0.5, 2.0)) / 0.71);
shado *= pow(-yc / cylinderRadius, 3.0);
shado *= 0.5;
otherColor = vec4(0.0, 0.0, 0.0, shado);
} else {
otherColor = texture2D(sourceTex, texCoord);
}
color = antiAlias(color, otherColor, cylinderRadius - abs(yc));
vec4 cl = seeThroughWithShadow(yc, texCoord, point, rotation, rrotation);
float dist = distanceToEdge(point);
gl_FragColor = antiAlias(color, cl, dist);
}
I just want to modify the angle of the curl but it seems like there are too many dependencies that are not made dynamicly, does anybody know the math behind it well enough to help me or knows maybe even a better code sample to use?

From RGB to HSV in OpenGL GLSL

I need to pass from RGB color space to HSV .. I searched in internet and found two different implementations, but those give me different results:
A:
precision mediump float;
vec3 rgb2hsv(float r, float g, float b) {
float h = 0.0;
float s = 0.0;
float v = 0.0;
float min = min( min(r, g), b );
float max = max( max(r, g), b );
v = max; // v
float delta = max - min;
if( max != 0.0 )
s = delta / max; // s
else {
// r = g = b = 0 // s = 0, v is undefined
s = 0.0;
h = -1.0;
return vec3(h, s, v);
}
if( r == max )
h = ( g - b ) / delta; // between yellow & magenta
else if( g == max )
h = 2.0 + ( b - r ) / delta; // between cyan & yellow
else
h = 4.0 + ( r - g ) / delta; // between magenta & cyan
h = h * 60.0; // degrees
if( h < 0.0 )
h += 360.0;
return vec3(h, s, v);
}
B:
precision mediump float;
vec3 rgb2hsv(float r, float g, float b) {
float K = 0.0;
float tmp;
if (g < b)
{
tmp = g;
g=b;
b=tmp;
K = -1.0;
}
if (r < g)
{
tmp = r;
r=g;
g=tmp;
K = -2.9 / 6.9 - K;
}
float chroma = r - min(g, b);
float h = abs(K + (g - b) / (6.0 * chroma + 1e-20));
float s = chroma / (r + 1e-20);
float v = r;
return vec3(h, s, v);
}
Do you know which is the correct implementation?
I am the author of the second implementation. It has always behaved correctly for me, but you wrote 2.9 / 6.9 instead of 2.0 / 6.0.
Since you target GLSL, you should use conversion routines that are written with the GPU in mind:
// All components are in the range [0…1], including hue.
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
 
// All components are in the range [0…1], including hue.
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
Taken from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl.
Edit: code is licensed under the WTFPL.
I don't have a development environment to check, but you can use wolframAlpha to build up some asserts.
For Instance: rgb(1,0,0)(pure red) to hsv is 0, 100%, 100% in hsv.