I am trying to do some raytracing in OpenGL via the compute shader and I came across a weird problem. At the moment I just want to display a sphere without any shading. My compute shader launches a ray for every pixel and looks like this:
#version 430
struct Sphere{
vec4 position;
float radius;
};
struct Ray{
vec3 origin;
vec3 dir;
};
uniform image2D outputTexture;
uniform uint width;
uniform uint height;
float hitSphere(Ray r, Sphere s){
float s_vv = dot(r.dir, r.dir);
float s_ov = dot(r.origin, r.dir);
float s_mv = dot(s.position.xyz, r.dir);
float s_mm = dot(s.position.xyz, s.position.xyz);
float s_mo = dot(s.position.xyz, r.origin);
float s_oo = dot(r.origin, r.origin);
float d = s_ov*s_ov-2*s_ov*s_mv+s_mv*s_mv-s_vv*(s_mm-2*s_mo*s_oo-s.radius*s.radius);
if(d < 0){
return -1.0f;
} else if(d == 0){
return (s_mv-s_ov)/s_vv;
} else {
float t1 = 0, t2 = 0;
t1 = s_mv-s_ov;
t2 = (t1-sqrt(d))/s_vv;
t1 = (t1+sqrt(d))/s_vv;
return t1>t2? t2 : t1 ;
}
}
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
void main(){
uint x = gl_GlobalInvocationID.x;
uint y = gl_GlobalInvocationID.y;
if(x < 1024 && y < 768){
float t = 0.0f;
Ray r = {vec3(0,0,0), vec3(width/2-x, height/2-y, 1000)};
Sphere sp ={vec4(0, 0, 35, 1), 5.0f};
t = hitSphere(r, sp);
if(t <= -0.001f){
imageStore(outputTexture, ivec2(x, y), vec4(0.0, 0.0, 0.0, 1.0));
} else {
imageStore(outputTexture, ivec2(x, y), vec4(0.0, 1.0, 0.0, 1.0));
}
if(x == 550 && y == 390){
imageStore(outputTexture, ivec2(x, y), vec4(1.0, 0.0, 0.0, 1.0));
}
}
}
When I run the application I get the following image:
But when I run the same algorithm on the CPU I get the following more convincing image:
First I thought I didn't dispatch enough work-groups so that not every pixel gets its own compute shader invocation but that's not the case. As you can see in the GPU rendered image there is a red pixel in the middle which is caused by the last lines in the compute shader. This can be reproduced for every other pixel.
I use a resolution of 1024x768 at the moment and this is how I dispatch my compute shader:
#define WORK_GROUP_SIZE 16
void OpenGLRaytracer::renderScene(int width, int height){
glUseProgram(_progID);
glDispatchCompute(width/WORK_GROUP_SIZE, height/WORK_GROUP_SIZE,1);
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
}
Where is the mistake? Might there be a problem with the accuracy of the floating point calculations?
The mistake is in this line:
Ray r = {vec3(0,0,0), vec3(width/2-x, height/2-y, 1000)};
Since width, height, x and y are unsigned variables you will get problems when the term width/2-x becomes negative.
This solves the problem:
Ray r = {vec3(0,0,0), vec3(float(width)/2.0f-float(x), float(height)/2.0f-float(y), 1000)};
Related
I am writing a basic Sphere-Tracer in a fragment shader, everything is doing fine if I just color points according to their surface normals, but as soon as I try to implement the reflecting algorithm, it takes a bit longer to compile, and just blackscreens. The fps starts out at 1 but quickly goes up to 1000, which is the code limit I put, telling me the shader is actually not doing anything and just ignores my code, which should be much slower than that. I initially thought I was hitting the instruction limit of my GPU, but I don't think that's the problem, as using the normal-shading code, I can set MAX_MARCH to high values, like 1000, but using reflections, even with MAX_REFLECTIONS and MAX_AA to one, which should be similar to the amount of instructions of normal-shading(maybe ~50 more, not significant I don't think). But I need to set MAX_MARCH to 1 for it to render, even setting it to two causes the bug.
Vertex shader:
//Draw a quad on the whole display and calculates sky color
#version 400 core
in vec3 position;
out vec2 uvPos;
out vec4 backColor;
void main(void){
gl_Position = vec4(position, 1.0);
uvPos = position.xy*0.5+0.5;
backColor = mix(vec4(1, 1, 1, 1), vec4(0.5, 0.7, 1, 1), uvPos.y);
}
Fragment shader:
#version 400 core
#define FLT_MAX 3.402823466e+38
#define FLT_MIN 1.175494351e-38
#define DBL_MAX 1.7976931348623158e+308
#define DBL_MIN 2.2250738585072014e-308
#define PI 3.141592653589793115997963468544185161590576171875
#define MAX_AA 1
#define MAX_MARCH 1000
#define MAX_REFLECTIONS 10
#define MAX_DIST 10
in vec2 uvPos;
in vec4 backColor;
out vec4 outColor;
int randomIterator = 0;
//############################################################ Structure definitions #########################################################################
struct Material{
int type;
vec3 albedo;
};
struct Object{
int type; //1:Sphere, 2:Box
vec3 center;
float radius;
vec3 size;
Material material;
};
struct Scene{
Object objects[3];
};
struct Ray{
vec3 origin;
vec3 dir;
};
struct HitRecord{
vec3 p;
vec3 n;
Object o;
Material mat;
float closest;
};
struct Camera{
vec3 origin;
vec3 lowerLeftCorner;
vec3 horizontal;
vec3 vertical;
};
//############################################################ Uniforms ####################################################################################
uniform float random[2048];
uniform vec2 resolution;
uniform Camera cam;
uniform Scene scene;
uniform int objectAmount;
//############################################################ Tools
float randf(){
return random[randomIterator++];
}
Ray getRay(Camera cam, vec2 v){
return Ray(cam.origin, normalize(cam.lowerLeftCorner+cam.horizontal*v.s+cam.vertical*v.t-cam.origin));
}
vec3 randOnBall(){
vec3 p;
do{
p = vec3(randf(), randf(), randf())*2-1;
}while(p.length() >= 1);
return p;
}
//############################################################ Signed Distance Functions
float sphereSDF(vec3 p, Object o){
return length(p-o.center)-o.radius;
}
float boxSDF(vec3 p, Object o){
vec3 q = abs(p-o.center) - o.size;
return (length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0));
}
float sceneSDF(vec3 p, Scene s){
float dist = FLT_MAX;
for(int i = 0; i < objectAmount; i++){
switch(s.objects[i].type){
case 1:
dist = min(dist, sphereSDF(p, s.objects[i]));
break;
case 2:
dist = min(dist, boxSDF(p, s.objects[i]));
break;
default:
break;
}
}
return dist;
}
float sceneSDF(vec3 p, Scene s, inout HitRecord rec){
float dist = FLT_MAX;
for(int i = 0; i < objectAmount; i++){
float tmpDist=FLT_MAX;
switch(s.objects[i].type){
case 1:
tmpDist = sphereSDF(p, s.objects[i]);
break;
case 2:
tmpDist = boxSDF(p, s.objects[i]);
break;
default:
break;
}
if(tmpDist<dist){
dist = tmpDist;
rec.o = s.objects[i];
rec.mat = s.objects[i].material;
}
}
return dist;
}
//############################################################ Material Scatter Function
bool scatterDiffuse(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
tmpAtt = vec3(rec.mat.albedo);
scattered = Ray(rec.p, rec.n+randOnBall());
return true;
}
bool scatter(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
return scatterDiffuse(r, rec, tmpAtt, scattered); //Starting out with diffuse materials, planned to
add switch-case for different materials
}
//############################################################ Main
vec3 findSceneNormal(Scene s, vec3 p){
const float h = 0.0001; // replace by an appropriate value
const vec2 k = vec2(1,-1);
return normalize( k.xyy*sceneSDF( p + k.xyy*h, s ) +
k.yyx*sceneSDF( p + k.yyx*h, s ) +
k.yxy*sceneSDF( p + k.yxy*h, s ) +
k.xxx*sceneSDF( p + k.xxx*h, s ) );
}
float findSceneIntersect(Ray r, Scene scene, inout HitRecord rec){
float t = 0.005;
vec3 p;
for(int i = 0; i < MAX_MARCH; i++){
p = r.origin+t*r.dir;
float dist = abs(sceneSDF(p, scene, rec));
if(dist < 0.001){
rec.n = findSceneNormal(scene, p);
rec.p = p;
return t;
}else{
t += dist;
if(t >= MAX_DIST){
rec.p = r.origin+t*r.dir;
rec.n = vec3(0, 0, 0);
return -1;
}
}
}
return -1;
}
vec3 calcColor(Ray r){
vec3 color;
Material emptyMat = Material(0, vec3(0));
Object emptyO = Object(0, vec3(0), 0, vec3(0), emptyMat);
HitRecord rec = HitRecord(vec3(0), vec3(0), emptyO, emptyMat, 0);
float t = findSceneIntersect(r, scene, rec);
int reflections = 0;
vec3 att = vec3(1, 1, 1);
for(int ref = 0; ref < MAX_REFLECTIONS; ref++){
if(t != -1){
vec3 tmpAtt = vec3(0);
if(scatter(r, rec, tmpAtt, r)){
att *= tmpAtt;
t = findSceneIntersect(r, scene, rec);
reflections++;
}else {
att *= tmpAtt;
t = -1;
}
}else {
color = backColor.xyz*att;
break;
}
}
return color;
}
void main(void){
HitRecord rec = HitRecord(vec3(0), vec3(0), Object(-1, vec3(0), 0, vec3(0), Material(-1, vec3(0))), Material(-1, vec3(1, 1, 1)), 0);
#if 1 //Reflection rendering
vec3 color = vec3(0);
for(int s = 0; s < MAX_AA; s++){
vec2 uv = uvPos+(vec2(randf(), randf())*2-1)/resolution;
color += calcColor(getRay(cam, uv));
}
outColor = vec4(color/MAX_AA, 1);
#else //Coloring based on normals
Ray r = getRay(cam, uvPos);
float t = findSceneIntersect(r, scene, rec);
if(t == -1){
outColor = backColor;
}else {
outColor = vec4(rec.n*0.5+0.5, 1);
}
#endif
}
Java code where I load and compile the shader (Using LWJGL):
private static int loadShader(String file, int type) {
System.out.println("Loading shader at path: " + file);
StringBuilder shaderSource = new StringBuilder();
try{
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while((line = reader.readLine())!=null){
shaderSource.append(line).append("//\n");
}
reader.close();
}catch(IOException e){
e.printStackTrace();
System.exit(-1);
}
int shaderID = glCreateShader(type);
glShaderSource(shaderID, shaderSource);
glCompileShader(shaderID);
if(glGetShaderi(shaderID, GL_COMPILE_STATUS )== GL_FALSE){
System.out.println(glGetShaderInfoLog(shaderID, 500));
System.err.println("Could not compile shader!");
System.exit(-1);
}
return shaderID;
}
The function loadShader in my code does not give me any error as it does with syntax errors and doesn't exit the program, thus GL_COMPILE_STATUS is not false.
I am fully aware nested for loops and my use of conditionals is far from efficient performance-wise, but I would expect it to be slow, not completely broken. I am running this on an Intel UHD 630, which according to https://www.intel.ca/content/www/ca/en/support/products/98909/graphics/graphics-for-7th-generation-intel-processors/intel-hd-graphics-630.html supports openGL 4.4. Therefore, according to GLSL maximum number of instructions, I should have access to 65536 instructions in my frag shader and fully dynamic branching. For these reasons, I don't think instruction limit is the problem and any help would be greatly appreciated. If you need any more information, I'll add it as soon as possible. If the CPU code is necessary, I can add it too, but I don't think it's the issue, as changing only the shader can trigger this bug.
Edit 1:
Both glGetShaderInfoLog and glGetProgramInfoLog return nothing when called after the program has been validated and linked.
I'm in the process of translating a piece of OpenGL code to Vulkan. The code recreates a rendered scene from an image (on a hemisphere projection) with depth information encoded. Note that I also load the model view matrix used for the projection to recreate the scene. The translation has been pretty straightforward but I'm running into issues due to the new Vulkan coordinate system.
The original OpenGL shader with comments follows:
#version 430
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
in vec2 posGeom[];
out vec2 texCoord;
uniform mat4 view;
uniform mat4 projection;
uniform float threshold;
uniform vec3 quantization;
uniform mat4 inverseStaticView;
uniform sampler2D rgbdTexture;
//get the image space for each pixel of our hemisphere image
vec3 getSphereRay(const vec2 coord) {
//get length of ray from camera to point on image plane
float len = 1 - coord.x * coord.x - coord.y * coord.y;
if (len > 0)
return vec3(coord, -sqrt(len));//scale to unit length vector as viewing ray
else
return vec3(0);
}
vec4 getPosition(const in vec2 inCoord, const in float depth) {
vec2 coord = inCoord;
//reverse the stretching from sphere to quad (based on y-coordinate)
float percent = sqrt(1.0 - coord.y * coord.y);
coord.x = coord.x * percent;
//scale ray with corresponding depth
vec3 normal = getSphereRay(coord) * depth;
//move from image space to world space by inverse view matrix
return inverseStaticView * vec4(normal, 1);
}
bool hasZeroDepth = false;
//get the real depth from quantized and packed depth by inverting the gamma correction and inverting min, max
float getDepth(int idx) {
float depth = texture(rgbdTexture, posGeom[idx] * 0.5 + 0.5).w;
if(depth == 0)
hasZeroDepth = true;
float minDepth = quantization.x;
float maxDepth = quantization.y;
float gamma = quantization.z;
depth = pow(depth, gamma);
depth = depth * (maxDepth - minDepth) + minDepth;
return depth;
}
//emit the position and texcoord
void emitPosition(int idx, float depth) {
texCoord = posGeom[idx] * 0.5 + 0.5;
gl_Position = projection * view * getPosition(posGeom[idx], depth);
EmitVertex();
}
void main() {
float d0 = getDepth(0);
float d1 = getDepth(1);
float d2 = getDepth(2);
//do not emit tris with zero (invalid) depth
if(!hasZeroDepth) {
float minDepth = min(d0, min(d1, d2));
float maxDepth = max(d0, max(d1, d2));
float minDist = maxDepth - minDepth;
float avgDepth = (d0 + d1 + d2) / 3;
float thres = threshold;
//look at tri stretching factor
if(minDist / avgDepth < thres) {
//emit original tri
emitPosition(0, d0);
emitPosition(1, d1);
emitPosition(2, d2);
} else {
//emit tri with maxDepth to only show background
emitPosition(0, maxDepth);
emitPosition(1, maxDepth);
emitPosition(2, maxDepth);
}
}
}
In the Vulkan shader, I account for the Vulkan coordinate system by inverting the y value. I also must normalize the world values for reasons that are unclear to me (otherwise what's rendered is completely nonsense). The shader code follows:
#version 450
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
layout(binding = 0) uniform UniformBufferObject {
mat4 modelView;
mat4 inverseStaticModelView;
float quantization;
} ubo;
layout(binding = 1) uniform sampler2D texSampler;
layout(location = 0) in vec2 posGeom[];
layout(location = 0) out vec2 texCoord;
bool hasZeroDepth = false;
float minDepth = 0;
float maxDepth = 1.0;
vec3 unproject(vec2 win) {
float scale = 1 - win.y * win.y;
// Invert y to account for Vulkan coordinate system.
float y = win.y * -1;
// Scale x to account for hemisphere projection.
float x = win.x * scale;
float z = -sqrt(1 - x * x - y * y);
if(z < 0){
vec4 outVals = ubo.inverseStaticModelView * vec4(x, y, z, 1.0);
return vec3(outVals[0], outVals[1], outVals[2]) / outVals.w;
}else
return vec3(0);
}
vec3 reconstructWorldPosition(vec2 ndc, float depth) {
vec3 pos = unproject(ndc);
return depth * normalize(pos);
}
float getDepth(int idx) {
float depth = texture(texSampler, posGeom[idx] * 0.5 + 0.5).w;
if(depth == 0)
hasZeroDepth = true;
depth = pow(depth, ubo.quantization);
return depth;
}
void emitPosition(int idx, float depth) {
vec2 pos = posGeom[idx].xy;
texCoord = pos * 0.5 + 0.5;
vec3 positionFromDepth = reconstructWorldPosition(pos, depth);
gl_Position = ubo.modelView * vec4(positionFromDepth,1);
EmitVertex();
}
void main() {
float d0 = getDepth(0);
float d1 = getDepth(1);
float d2 = getDepth(2);
if(!hasZeroDepth) {
float minDepth = min(d0, min(d1, d2));
float maxDepth = max(d0, max(d1, d2));
float minDist = maxDepth - minDepth;
float avgDepth = (d0 + d1 + d2) / 3.0;
float thres = 0.1;
if(minDist / avgDepth < thres ) {
emitPosition(0, d0);
emitPosition(1, d1);
emitPosition(2, d2);
} else {
emitPosition(0, maxDepth);
emitPosition(1, maxDepth);
emitPosition(2, maxDepth);
}
}
}
Images of the output of the two programs are contained in this album: http://imgur.com/a/KUl57
The Vulkan output appears to almost be correct except for some odd artifacts in the lower left hand of the scene. My suspicion is that the scaling to the x coordinate to account for the hemisphere projection is causing the issue. I've played around with the scaling and other parts of the shader but I can't seem to get it right. Am I overlooking something else that is different between Vulkan and OpenGL, especially with regards to the coordinate system?
I want to ask a question about my lighting effect in OpenGL.
I am trying to add lighting, but I don't think it's good and I've seen some 2D lighting pictures which are so much better than mine.
Question: I have made a spotlight but I want it to be dimmer as its light range gets lower and have it more like a natural light, but I can't figure out the solution.
I am using an orthographic matrix with (800, 600) as the window size and I make my meshes with real x, y coords. I send my lightPos and my PlayerPos to the fragment shader and I use the vertex as the width and the height of the mesh so that I can generate lighting for every pixel.
The light is just a basic circle and I don't know how to make it look better. Here are some images. In the fragment shader, I use the Pythagorean Theorem to calculate the distance between the 2 points.
And here is the vertex and fragment Shader
Vetex shader
#version 330
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tCoord;
uniform mat4 mat;
out vec2 tCoord0;
out vec2 vPos;
void main(){
tCoord0 = vec2(tCoord.x, 1 - tCoord.y);
gl_Position = mat * vec4(pos, 1.0);
vPos = vec2(pos.x, pos.y);
}
Fragment shader
#version 330
out vec4 color;
uniform sampler2D sampler;
in vec2 tCoord0;
uniform vec3 objColor;
uniform vec2 lightPos;
uniform vec2 xyPos;
in vec2 vPos;
void main(){
vec4 textureColor = texture2D(sampler, tCoord0);
vec3 ambientLight = vec3(0.3f, 0.3f, 0.3f);
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
if(dist > 0 && dist < 50){
ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70){
ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}
else{
discard;
}
if((textureColor.x == 0 && textureColor.y == 0 && textureColor.z == 0) || textureColor.a <= 0){
color = vec4(objColor, 1.0) * vec4(ambientLight, 1.0);
}
else{
color = textureColor * vec4(ambientLight, 1.0) * vec4(objColor, 1.0);
}
}
Drawer.cpp
#include <graphics\shader.h>
#include <graphics\texture.h>
#include <graphics\shape.h>
#include <GL\glew.h>
#include <graphics\light.h>
#include <core\TSAContainer.h>
#include <core\drawer.h>
namespace GE{
namespace core{
std::vector<graphics::GraphicComponent*> Drawer::drawables;
GLuint Drawer::buffer;
void Drawer::init(){
glGenFramebuffers(1, &buffer);
}
std::vector<graphics::GraphicComponent*>& Drawer::getAllGraphicComponents(){
return drawables;
}
void Drawer::addDrawable(graphics::GraphicComponent* drawable){
drawables.push_back(drawable);
}
void Drawer::destroy(){
for (unsigned int i = 0; i < drawables.size(); i++)
delete drawables[i];
drawables.clear();
}
void Drawer::render(){
for (std::vector<graphics::GraphicComponent*>::iterator it = drawables.begin(); it != drawables.end(); it++){
if ((*it)->isDraw()){
(*it)->getShader().bind();
int color = getColor(static_cast<graphics::Shape*>(*it)->getColor());
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = (color)& 0xff;
(*it)->getShader().setUniform("mat", (*it)->getTransformation().getTransformationMatrix());
(*it)->getShader().setUniform("objColor", r, g, b);
(*it)->getShader().setUniform("xyPos", (*it)->getTransformation().getPosition());
(*it)->getShader().setUniform("sampler", 1);
if (static_cast<graphics::Shape*>(*it)->getLight() != NULL){
static_cast<graphics::Shape*>(*it)->getLight()->update();
}
//(*it)->getShader().setUniform("ambientLight", static_cast<graphics::Shape*>(*it)->getAmbientLight());
glActiveTexture(GL_TEXTURE1);
if ((*it)->getTexture() != NULL)
(*it)->getTexture()->bind();
(*it)->getMesh().draw();
if ((*it)->getTexture() != NULL)
(*it)->getTexture()->unbind();
(*it)->getShader().unbind();
}
}
}
int Drawer::getColor(colorType color){
int col = 0;
if (color == GE_COLOR_BLUE){
col = 0 << 16 | 0 << 8 | 1;
}
else if (GE_COLOR_GREEN == color){
col = 0 << 16 | 1 << 8 | 0;
}
else if (GE_COLOR_RED == color){
col = 1 << 16 | 0 << 8 | 0;
}
else{
col = 1 << 16 | 1 << 8 | 1;
}
return col;
}
Drawer::Drawer(){
}
Drawer::~Drawer(){
}
}
}
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
if(dist > 0 && dist < 50)
{
ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70)
{
ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}
Here you're using kind of a constant attenuation based on distance. That's going to make that kind of effect of a bright inner circle and dim outer circle with unnaturally hard edges between.
If you want a soft kind of gradient effect, you want to avoid the branching and constants here. We can start with a linear falloff:
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
float max_dist = 70.0f;
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
ambientLight = vec3(percent, percent, percent);
However, that will probably look kind of ugly to you with a sharp point around the center. We can use an exponential curve instead, like so:
...
percent *= percent;
ambientLight = vec3(percent, percent, percent);
To make it kind of "rounder", you can multiply again:
...
percent *= percent * percent;
ambientLight = vec3(percent, percent, percent);
If that's kind of opposite of what you want visually, you can try sqrt:
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
percent = sqrt(percent);
Since I don't know exactly what you're after visually, these are some things to try initially. Play with these two and see if you like what you get.
If you really want to take max control over the effect, a cubic bezier curve interpolation might come in handy:
float bezier4(float p1, float p2, float p3, float p4, float t)
{
const float mum1 = 1.0f - t;
const float mum13 = mum1 * mum1 * mum1;
const float mu3 = t * t * t;
return mum13 * p1 + 3 * t * mum1 * mum1 * p2 + 3 * t * t * mum1 * p3 + mu3 * p4;
}
...
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
// Can play with the first four arguments to achieve the desired effect.
percent = bezier4(0.0f, 0.25f, 0.75f, 1.0f, percent);
ambientLight = vec3(percent, percent, percent);
That will give you a lot of control over the effect, but maybe overkill. Try the other methods first.
I have a volume rendering program write by OpenGL and GLSL.Now I need calculate a fragment's depth value .The follow code is my fragment shader program
#version 120
//
some needed variable declaration
//
float calculateDepthValue(float t, float entryPointsDepth, float exitPointsDepth)
{
// assign front value given in windows coordinates
float zw_front = entryPointsDepth;
// and convert it into eye coordinates
float ze_front = 1.0 / ((zw_front - const_to_z_e_1)*const_to_z_e_2);
// assign back value given in windows coordinates
float zw_back = exitPointsDepth;
// and convert it into eye coordinates
float ze_back = 1.0 / ((zw_back - const_to_z_e_1)*const_to_z_e_2);
// interpolate in eye coordinates
float ze_current = ze_front + t*(ze_back - ze_front);
// convert back to window coordinates
float zw_current = (1.0 / ze_current)*const_to_z_w_1 + const_to_z_w_2;
return zw_current;
}
float getDepthValue(float t, float tEnd, float entryPointsDepth, float exitPointsDepth)
{
if (t >= 0.0)
return calculateDepthValue(t / tEnd, entryPointsDepth, exitPointsDepth);
else
return 1.0;
}
vec4 compositeDVR(in vec4 curResult, in vec4 color,in float t, in float pvar,inout float tDepth)
{
vec4 result = curResult;
// apply opacity correction to accomodate for variable sampling intervals
color.a = 1.0 - pow(1.0 - color.a, pvar);
result.rgb = result.rgb + (1.0 - result.a) * color.a * color.rgb;
result.a = result.a + (1.0 - result.a) * color.a;
// save first hit ray parameter for depth value calculation
if (tDepth < 0.0)
tDepth = t;
return result;
}
void main()
{
vec2 p = gl_FragCoord.xy * screenDimRCP_;
vec3 start = texture2D(frontTex, p).rgb;
vec3 end = texture2D(texBack, p).rgb;
float entryDepth = texture2D(entryPointsDepth_, p).z;
float exitDepth = texture2D(exitPointsDepth_, p).z;
//
some needed code
//
lookup = texture1D(globalTex, sample);
if (lookup.a > 0.0)
{
lookup.rgb = phongShading(N, texToPhysicalPos, position_, cPos, lookup.rgb, lookup.rgb, vec3(1.0, 1.0, 1.0));
//compositeDVR
result = compositeDVR(result, lookup, t, powAlp, tDepth);
}
gl_FragDepth = getDepthValue(tDepth, tEnd, entryDepth, exitDepth);
gl_FragColor = result;
}
In fragment shader,some function come from Voreen's code.my program's result is that gl_FragDepth all equal 1,this is wrong.any people can help me?
I'm currently learning about shaders and graphics pipelines and I was wondering if a pixel shader could be used to create, for example, a triangle or a more complex shape like a zigzag.
Could this be done without the use of a vertex shader?
Answer is yes! You can draw anything you want using pixel shader by implementing a ray Tracer. Here is a sample code:
uniform vec3 lightposition;
uniform vec3 cameraposition;
uniform float motion;
struct Ray
{
vec3 org;
vec3 dir;
};
struct Sphere
{
vec3 Center;
float Radius;
vec4 Color;
float MatID;
float id;
};
struct Intersection
{
float t;
vec3 normal;
vec3 hitpos;
vec4 color;
float objectid;
float materialID;
};
bool sphereIntersect(Ray eyeray, Sphere sp, inout Intersection intersection)
{
float t1=0.0;
eyeray.dir = normalize(eyeray.dir);
float B = 2.0 *( ( eyeray.dir.x * (eyeray.org.x - sp.Center.x ) )+ ( eyeray.dir.y *(eyeray.org.y - sp.Center.y )) + ( eyeray.dir.z * (eyeray.org.z - sp.Center.z ) ));
float C = pow((eyeray.org.x - sp.Center.x),2.0) + pow((eyeray.org.y - sp.Center.y),2.0) + pow((eyeray.org.z - sp.Center.z),2.0) - pow(sp.Radius,2.0);
float D = B*B - 4.0*C ;
if(D>=0.0)
{
t1= (-B - pow(D, .5)) / 2.0;
if (t1 < 0.0)
{
t1 = (-B + pow(D, .5)) / 2.0;
if( t1 < 0.0)
return false;
else
{
if (t1 > 1e-2 && t1 < intersection.t)
{
intersection.t = t1;
intersection.materialID = sp.MatID;
intersection.hitpos = eyeray.org + t1 * eyeray.dir;
intersection.normal = normalize(intersection.hitpos - sp.Center);
intersection.color = sp.Color;
intersection.objectid = sp.id;
return true;
}
}
}
else
{
if(t1 > 1e-2 && t1 < intersection.t)
{
intersection.t = t1;
intersection.materialID = sp.MatID;
intersection.hitpos = eyeray.org + t1 * eyeray.dir;
intersection.normal = normalize(intersection.hitpos - sp.Center);
intersection.color = sp.Color;
intersection.objectid = sp.id;
return true;
}
}
}
else
return false;
}
void findIntersection(Ray ray, inout Intersection intersection)
{
intersection.t = 1e10;
intersection.materialID = 0.0;
Sphere sp1 = Sphere(vec3(-2.0,0.0,-5.0),1.5,vec4(0.5, 0.1, 0.5, 1.0),1.0,1.0);
Sphere sp2 = Sphere(vec3( 2.0,0.0,-5.0),1.5,vec4(0.5,0.5,0.1,1.0),1.0,2.0);
Sphere sp3 = Sphere(vec3( 0.0,3.0,-5.0),1.5,vec4(0.1,0.5,0.5,1.0),1.0,3.0);
sphereIntersect(ray, sp1, intersection);
sphereIntersect(ray, sp2, intersection);
sphereIntersect(ray, sp3, intersection);
}
vec4 CalculateColor(vec4 ambient ,float shiness,vec3 intersection, vec3 normal);
Ray ReflectedRay(vec3 Normal,Ray EyeRay,vec3 intersection);
vec4 GetColor(Ray ray)
{
Ray currentRay = ray;
vec4 finalColor = vec4(0.0);
for(int bounce = 1 ; bounce < 4 ; bounce++)
{
Intersection intersection;
intersection.objectid = 0.0;
findIntersection(currentRay, intersection);
if (intersection.materialID == 0.0) // We could not find any object. We return the background color
return finalColor;
else if (intersection.materialID == 1.0)
{
vec3 lv = lightposition - intersection.hitpos;
vec3 nlv = normalize(lv);
Intersection shadowIntersection;
Ray shadowRay = Ray(intersection.hitpos, nlv);
shadowIntersection.objectid = intersection.objectid;
findIntersection(shadowRay, shadowIntersection);
if (shadowIntersection.t > length(lv) || shadowIntersection.t < 1)
{
finalColor = finalColor + float(1.0f/bounce) * CalculateColor(intersection.color, 100.0, intersection.hitpos, intersection.normal);;
}
else
{
finalColor = finalColor + float(1.0f/bounce) * intersection.color;
}
//currentRay = Ray(intersection.hitpos, reflect(ray.dir, intersection.normal));
currentRay = ReflectedRay(intersection.normal,ray,intersection.hitpos);
}
}
return finalColor;
}
Ray createRay(float ScreenWidth,float ScreenHeight)
{
Ray toret;
toret.org = cameraposition;
float left = -3.0;
float bottom = -3.0;
float screenZ = -3.0;
float su = -3.0 + gl_FragCoord.x/ScreenWidth * 6; //gl_FragCoord gives you the current x and y component of your current pixel
float sv = -3.0 + gl_FragCoord.y/ScreenHeight * 6;
float sz = screenZ - cameraposition.z;
toret.dir = normalize(vec3(su,sv,sz));
//vec2 p = (gl_FragCoord.xy/resolution) * 2 ;
//toret.dir = normalize(vec3(p, -1.0));
return toret;
}
Ray ReflectedRay(vec3 Normal,Ray EyeRay,vec3 intersection)
{
Ray reflection;
reflection.dir = EyeRay.dir - 2 * Normal * dot(EyeRay.dir,Normal);
reflection.org = intersection + reflection.dir * 0.01;
return reflection;
}
vec4 CalculateColor(vec4 ambient ,float shiness,vec3 intersection, vec3 normal)
{
//intensities
vec3 Idifuse = vec3(1, 1, 1);
vec3 Iambient = vec3(0.8, 0.8, 0.8);
vec3 Ispecular = vec3(1,1,1);
vec3 kDifuse = vec3(0.5,0.5,0.5); //for difuse
vec3 kSpecular = vec3(0.75, 0.6, 0.3); //for specular
vec3 kAmbient = vec3(0.1, 0.2, 0.3); //for ambient
//vec4 kSpecular = vec4(0.5,0.5,0.5,1.0);
//vec4 kDifuse = vec4(0.5,0.5,0.5,1.0);
float ColorDifuse = max(dot(normal,lightposition),0.0) * kDifuse;
//vector calculations
vec3 l = normalize(lightposition - intersection); //light vector
vec3 n = normalize(normal); // normalVector of point in the sea
vec3 v = normalize(cameraposition - intersection); // view Vector
vec3 h = normalize(v + l); // half Vector
vec3 difuse = kDifuse * Idifuse * max(0.0, dot(n, l));
vec3 specular = kSpecular * Ispecular * pow(max(0.0, dot(n, h)), shiness);
vec3 color = ambient.xyz + difuse + specular;
return vec4(color,1.0);
gl_FragColor = vec4(color,1.0);
}
void main()
{
if(lightposition == vec3(0.0,0.0,0.0))
gl_FragColor = vec4(0.0,1.0,0.0,1.0);
Ray eyeray = createRay(600.0,600.0);
gl_FragColor = GetColor(eyeray);
}
A useful technique is to use a fragment shader (I'm an OpenGL guy) with point sprites. Point sprites in OpenGL 3+ get rendered as squares of pixels, with the size of the square (gl_PointSize) set by the vertex shader.
In the fragment shader, gl_PointCoord has the x and y coords of this particular pixel within the square, from 0.0 to 1.0. So you can draw a circle by testing if gl_PointCoord.x and gl_PointCoord.y are both within the radius and discarding if not, a framed square by checking that .x and .y are with some distance of the edge, and so on. It's classic maths, define a function(x, y) which returns true for points within the shape you want, false if not.
The Orange book, OpenGL Shading Language 3rd edition, has some examples (which in turn come from RenderMan) of how to draw such shapes.
Hope this helps.
What you want is called procedural textures or procedural shading.
You can draw different shapes with a simple (and not so simple) math.
Take a look for some examples here:
http://glslsandbox.com/
More on google.