I've created a brdf file but then I try to open it in BRDF explorer
in the brdf.exe it appears the following message.
BRDF Version 1.0.0
opening ./probes/beach.penv
Unrecognised OpenGL version
My code had some problems but with help from team stackoverflow I fixed.
however there is still one problem.
Can anyone advise how to fix this?
this is my code
analytic
::begin parameters
float baseColor 0.001 .2 .1
float m 0.001 0.1 1.0
::end parameters
::begin shader
const float PI = 3.14;
float Fresnel(float baseColor, float l, float h)
{
return baseColor + ( 1 - baseColor ) * pow( 1 - (h), 5);
}
float ggx_visib(float dotNV, float m2)
{
return 1.0/(dotNV*(1.0-m2)+m2);
}
vec3 BRDF( vec3 N, vec3 V, vec3 L, float m, float baseColor)
{
vec3 H = normalize(V+L);
float dotNL = clamp(dot(N,L), 0.0, 1.0);
float dotLH = clamp(dot(L,H), 0.0, 1.0);
float dotNH = clamp(dot(N,H), 0.0, 1.0);
float DotNH2 = dotNH * dotNH;
float m2 = m * m;
float D = m2 / (PI * pow(dotNH * dotNH * (m2 - 1) + 1, 2.0));
float F = Fresnel(baseColor, dotNH);
float g1o =ggx_visib(m, dotNH);
float g1o =ggx_visib(m, dotNV);
float G = g1i * g1o;
D * G * F * (1.0 / (4.0 * dotNL * dotNH));
}
::end shader
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
I am trying to implement atmospheric scatting in GLSL version 4.10. I am adapting the shaders from the this Shadertoy shader https://www.shadertoy.com/view/lslXDr. The atmosphere in my program is created from a scaled version of the planet sphere.
I have the actual scattering equations working, but the inner radius of the atmosphere does not line up with the outer radius of the sphere for most camera positions. I know this is from the radius of the atmosphere being bigger than the planet sphere, but I cannot seem to get it to scale right.
My problem is best illustrated here. The model is scaled up in these pictures. As can be seen, the atmosphere inner radius does not match the radius of the planet (the dark blue sphere).
Here the model is scaled and translated. The atmosphere is off center from the camera and the inner atmosphere is still not lined up with the planet.
Here is the vertex shader, which is essentially a pass through shader
#version 410
in vec4 vPosition;
in vec3 vNormal;
out vec3 fPosition;
out mat3 m;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
fPosition = vec3(vPosition);
m = mat3(model);
gl_Position = projection*view*model*vPosition;
}
And the fragment shader.
#version 410
uniform float time;
uniform vec3 camPosition;
uniform float fInnerRadius;
uniform float fOuterRadius;
in vec3 fPosition;
in mat3 m;
out vec4 FragColor;
const float PI = 3.14159265359;
const float degToRad = PI / 180.0;
const float MAX = 10000.0;
float K_R = 0.166;
const float K_M = 0.0025;
const float E = 14.3;
const vec3 C_R = vec3(0.3, 0.7, 1.0);
const float G_M = -0.85;
float SCALE_H = 4.0 / (fOuterRadius - fInnerRadius);
float SCALE_L = 1.0 / (fOuterRadius - fInnerRadius);
const int numOutScatter = 10;
const float fNumOutScatter = 10.0;
const int numInScatter = 10;
const float fNumInScatter = 10.0;
vec3 rayDirection(vec3 camPosition) {
vec3 ray = m*fPosition - camPosition;
float far = length(ray);
return ray /= far;
}
vec2 rayIntersection(vec3 p, vec3 dir, float radius ) {
float b = dot( p, dir );
float c = dot( p, p ) - radius * radius;
float d = b * b - c;
if ( d < 0.0 ) {
return vec2( MAX, -MAX );
}
d = sqrt( d );
float near = -b - d;
float far = -b + d;
return vec2(near, far);
}
// Mie
// g : ( -0.75, -0.999 )
// 3 * ( 1 - g^2 ) 1 + c^2
// F = ----------------- * -------------------------------
// 2 * ( 2 + g^2 ) ( 1 + g^2 - 2 * g * c )^(3/2)
float miePhase( float g, float c, float cc ) {
float gg = g * g;
float a = ( 1.0 - gg ) * ( 1.0 + cc );
float b = 1.0 + gg - 2.0 * g * c;
b *= sqrt( b );
b *= 2.0 + gg;
return 1.5 * a / b;
}
// Reyleigh
// g : 0
// F = 3/4 * ( 1 + c^2 )
float rayleighPhase( float cc ) {
return 0.75 * ( 1.0 + cc );
}
float density(vec3 p) {
return exp(-(length(p) - fInnerRadius) * SCALE_H);
}
float optic(vec3 p, vec3 q) {
vec3 step = (q - p) / fNumOutScatter;
vec3 v = p + step * 0.5;
float sum = 0.0;
for(int i = 0; i < numOutScatter; i++) {
sum += density(v);
v += step;
}
sum *= length(step)*SCALE_L;
return sum;
}
vec3 inScatter(vec3 o, vec3 dir, vec2 e, vec3 l) {
float len = (e.y - e.x) / fNumInScatter;
vec3 step = dir * len;
vec3 p = o + dir * e.x;
vec3 v = p + dir * (len * 0.5);
vec3 sum = vec3(0.0);
for(int i = 0; i < numInScatter; i++) {
vec2 f = rayIntersection(v, l, fOuterRadius);
vec3 u = v + l * f.y;
float n = (optic(p, v) + optic(v, u))*(PI * 4.0);
sum += density(v)* exp(-n * ( K_R * C_R + K_M ));
v += step;
}
sum *= len * SCALE_L;
float c = dot(dir, -l);
float cc = c * c;
return sum * ( K_R * C_R * rayleighPhase( cc ) + K_M * miePhase( G_M, c, cc ) ) * E;
}
void main (void)
{
vec3 dir = rayDirection(vec3(camPosition.x, 0.0, camPosition.z));
vec3 eye = vec3(camPosition.x, 0.0, camPosition.z);
vec3 l = normalize(vec3(0.0, 0.0, 1.0));
vec2 e = rayIntersection(eye, dir, fOuterRadius);
if ( e.x > e.y ) {
discard;
}
vec2 f = rayIntersection(eye, dir, fInnerRadius);
e.y = min(e.y, f.x);
vec3 I = inScatter(eye, dir, e, l);
FragColor = vec4(I, 1.0);
}
If needed here is the code that draws the atmosphere. The code that draws the planet has essentially the same transformations sans the scaleFactor.
void drawAtmosphere()
{
glUseProgram(atmosphereShader);
v = getViewMatrix();
vec3 Position = getCameraPosition();
float scaleFactor = 1.25;
m = multiplymat4(translate(0.0, 0.0, -10), scale(fScale*scaleFactor));
float fOuter = (fScale*scaleFactor);
float fInner = fScale;
glUniform1f(glGetUniformLocation(atmosphereShader, "fInnerRadius"), fInner);
glUniform1f(glGetUniformLocation(atmosphereShader, "fOuterRadius"), fOuter);
glUniform3f(glGetUniformLocation(atmosphereShader, "camPosition"), Position.x, Position.y, Position.z);
glUniform1f(glGetUniformLocation(atmosphereShader, "time"), glfwGetTime());
initMVP(atmosphereShader, m, v);
glBindVertexArray (atmosphereVAO);
glDrawArrays( GL_TRIANGLES, 0, planet.vertexNumber);
glBindVertexArray(0);
}
Any help, or anything that can point me in the right direction is appreciated.
Found the problem was caused by incorrect calculation of the camera position and not taking into account the model space of the object. I uploaded a stripped down version of the code here.
Hopefully this will help anyone trying to implement Sean O'Neil's atmosphere code.
I'm trying to render a Mandelbrot Set using GLSL, but all I get is a full circle...
I have checked the maths a lot, and I simply cannot find the error, so I thought maybe the problem was semantic.
Is anyone able to see what's wrong?
Also, could anyone give me insights on organization, structure, etc? I'm trying to learn proper coding, but it's hard to find material on styling.
Obs.: The shader can be applied over any image
The idea is simple (you may skip this):
checkConvergence returns true if z has not diverged (i.e., abs(z) < 4
sumSquare returns the complex multiplication (z.r, z.i)*(z.r, z.i) - (c.r, c.i), where K.r = Re(K) and K.i = Im(K)
iterate is the actual algorithm: it keeps squaring z until it diverges or it reaches a maximum number of iterations (tol)
clr is parameterized function which returns a RGB array which depends on n
finally, effect is where the magic should happen, by doing 2 things:
It takes the coordinates of the pixel (GLSL normalizes to the interval(0, 1) and normalizes it to the Mandelbrot set size (-2
It calls clr over iterate over the coordinates.
GLSLShader = love.graphics.newShader[[
vec2 c = vec2(1.0, 0.0);
int tol = 100;
vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
bool checkConvergence(vec2 z) {
return (z[0]*z[0] + z[1]*z[1] < 4);
}
vec2 sumSquare(vec2 z) {
return vec2(z[0]*z[0] - z[1]*z[1] - c[0], 2 * z[0] * z[1] - c[1]);
}
int iterate(vec2 z) {
int n = 0;
while (checkConvergence(z) && (n < tol)) {
vec2 z = sumSquare(z);
n = n + 1;
}
return n;
}
vec4 clr(int n){
if(n == tol){return vec4(0.0,0.0,0.0,1.0);}
int r, g, b;
r = int(255*n + 47*(1-n));
g = int(180*n + 136*(1-n));
b = int(38*n + 255*(1-n));
if (r > 255) {r = 255;}
else{
if(r<0){r = 0;}
}
if (g > 255) {g = 255;}
else{
if(g<0){g = 0;}
}
if (b > 255) {b = 255;}
else{
if(b<0){b = 0;}
}
return vec4(r, g, b, 1.0);
}
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords){
vec2 z = vec2(texture_coords.x*4-2, texture_coords.y*4-2);
return clr(iterate(z));
}
]]
UPDATE
I've tried some suggestions from #WeatherVane:
Add C instead of subtracting it;
Start z as (0.0, 0.0) and pass the starting point of the iteration as C;
But all to no avail, I still get a circle. I also tried using more iterations
I tried to simplify the coding without changing much.
I added a condition to the clr function, which returns green if n is smaller than 0 or greater than 1. The strange thing is that the circle is black and the rest of the screen is white, but I don't see where this white can come from (since clr never returns white for 0 < n < 1 ).
vec2 c = vec2(0.0, 0.0);
int tol = 1000;
vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
int iterate(vec2 z) {
int n = 0;
while ( (z[0]*z[0] + z[1]*z[1] < 4) && (n < tol) ) {
vec2 z = vec2( z[0]*z[0] - z[1]*z[1] + c[0], 2 * z[0] * z[1] + c[1] );
n = n + 1;
}
return n;
}
vec4 clr(int n){
n = n / tol;
if(n == 1){return black;}
if(n > 1 || n < 0){return green;}
int r, g, b;
r = int(255*n + 47*(1-n));
g = int(180*n + 136*(1-n));
b = int(38*n + 255*(1-n));
return vec4(r, g, b, 1.0);
}
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords){
vec2 z = vec2(texture_coords.x*4-2, texture_coords.y*4-2);
return clr(iterate(z));
}
The starting point of the iteration should be passed as vector c, while z start from {0.0.0.0}.
As you can find in https://en.wikipedia.org/wiki/Mandelbrot_set
a complex number c is part of the Mandelbrot set if, when starting
with z = 0 and applying the iteration z = z² + c repeatedly, the
absolute value of z remains bounded however large n gets.
You are using vec4 for colors but instead of using float, in your code you are calculating the RGB component with integers value. You should cast to float and normalize each component to the (0.0,1.0) range. I tried to correct your code, but I'm afraid I don't really know lua nor love2d and I wasn't able to use texture_coords, so I used screen_coords. The best I could do is this:
function love.load()
GLSLShader = love.graphics.newShader[[
vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
int max_iter = 1024;
vec4 clr(int n){
if(n == max_iter){return black;}
float m = float(n)/float(max_iter);
float r = float(mod(n,256))/32;
float g = float(128 - mod(n+64,127))/255;
float b = float(127 + mod(n,64))/255;
if (r > 1.0) {r = 1.0;}
else{
if(r<0){r = 0;}
}
if (g > 1.0) {g = 1.0;}
else{
if(g<0){g = 0;}
}
if (b > 1.0) {b = 1.0;}
else{
if(b<0){b = 0;}
}
return vec4(r, g, b, 1.0);
}
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords){
vec2 c = vec2((screen_coords[0]-500)/200,(screen_coords[1]-300)/200);
vec2 z = vec2(0.0,0.0);
vec2 zn = vec2(0.0,0.0);
int n_iter = 0;
while ( (z[0]*z[0] + z[1]*z[1] < 4) && (n_iter < max_iter) ) {
zn[0] = z[0]*z[0] - z[1]*z[1] + c[0];
zn[1] = 2*z[0]*z[1] + c[1];
z[0] = zn[0];
z[1] = zn[1];
n_iter++;
}
return clr(n_iter);
}
]]
end
function love.draw()
love.graphics.setShader(GLSLShader)
love.graphics.rectangle('fill', 0,0,800,600)
love.graphics.setShader()
end
Which gave me this output:
while ( ... ) {
vec2 z = ...
}
Get rid of vec2, you're declaring a new variable each iteration. Besides that, follow some of #Bob__ 's advice (ie. iterate(vec2 c)).