I've written a raytracer in C++. This is the snippet for calculating the diffuse component:
//diffuse component
color diffuse(0, 0, 0);
if (intrs.mat.diffuseness > 0)
{
for (auto &light : lights)
{
//define ray from hit object to light
ray light_dir(intrs.point, (light->point - intrs.point).normalize());
double nl = light_dir.direction*intrs.normal; //dot product
double diminish_coeff = 1.0;
double dist = intrs.point.sqrDistance(light->point);
//check whether it reaches the light
if (nl > 0)
{
for (int i = 0; i < (int)shapes.size(); ++i)
{
shape::intersection temp_intrs(shapes[i]->intersect(light_dir, shapes[i]->interpolate_normals));
if (temp_intrs.valid && temp_intrs.point.sqrDistance(intrs.point) < dist)
{
diminish_coeff *= shadow_darkness;
break;
}
}
}
diffuse += intrs.mat.diffuseness * intrs.mat.col * light->light_color * light->light_intensity * nl*diminish_coeff;
}
}
Of course, I can't post the entire code, but I think it should be clear what I'm doing here - intrs is the current intersection of a ray and object and shapes is a vector of all objects in the scene.
Colors are represented as RGB in the (0,1) range. Addition and multiplication of colors are simple memberwise addition and multiplication. Only when the raytracing is over, and I want to write into the image file, I multiply my colors by 255 and clamp to 255 if a component is larger than that.
Currently, there is one point light in the scene and it's white: color(1,1,1), intensity = 1.0.
This is my rendered image:
So, this is not right - the cupboard on the left is supposed to be green, and the box is supposed to be red.
Is there something obviously wrong with my implementation? I can't seem to figure it out. I'll post some more code if necessary.
It seems that your diffuse += line should be inside the if (nl > 0) condition, not outside it.
I found the problem. For some reason, my intrs.normal vector wasn't normalized. Thank you everyone for your help.
Related
I've implemented a simple ray tracer and now I'm trying to implement reflections but objects are behaving as transparent.
Here is my code for getting the reflected ray.
ray* reflected = new ray();
reflected->direction = rayIn.direction - (2 * glm::dot(rayIn.direction, normal)) * normal;
reflected->origin = int_point + epsilon * normal;
outColor += ((int_object->reflectivity)*intersectray(*reflected, depth - 1));
Here are images With code:
Without code:
I'll edit the post if more code is needed.
Edit : It seems the problem is when I'm iterating through the objects in the scene. I insert the objects as
scene->add(sphere1);
scene->add(sphere2);
But when I change this to :
scene->add(sphere2);
scene->add(sphere1);
the output is correct.
Sphere 1 is at closer to camera than sphere 2 and they are not overlapping.
Problem was this part of code
for (objects in scene){
double intersection = (*objIterator)->intersect(rayIn,normal);
if (intersection < minDistance && intersection > epsilon )
{
minDistance = intersection;
int_object = *objIterator;
int_point = rayIn.origin + intersection * rayIn.direction + (epsilon * normal);
}}
Here normal is used later for other calculations but the first line update normal for current object intersection (Even if its not close). So I added a vector to store normal of the intersection object and used it later.
I've started coding a raytracer, but today I encounter a problem when dealing with reflection.
First, here is an image of the problem:
I only computed the object's reflected color (so no light effect is applied on the reflected object)
The problem is that distortion that I really don't understand.
I looked at the angle between my rayVector and the normalVector and it looks ok, the reflected vector also looks fine.
Vector Math::calcReflectedVector(const Vector &ray,
const Vector &normal) const {
double cosAngle;
Vector copyNormal = normal;
Vector copyView = ray;
copyNormal.makeUnit();
copyView.makeUnit();
cosAngle = copyView.scale(copyNormal);
return (-2.0 * cosAngle * normal + ray);
}
So for example when my ray is hitting the bottom of my sphere I have the following values:
cos: 1
ViewVector: [185.869,-2.44308,-26.3504]
NormalVector: [185.869,-2.44308,-26.3504]
ReflectedVector: [-185.869,2.44308,26.3504]
Bellow if the code that handles the reflection:
Color Rt::getReflectedColor(std::shared_ptr<SceneObj> obj, Camera camera,
Vector rayVec, double k, unsigned int pass) {
if (pass > 10)
return obj->getColor();
if (obj->getReflectionIndex() == 0) {
// apply effects
return obj->getColor();
}
Color cuColor(obj->getColor());
Color newColor(0);
Math math;
Vector view;
Vector normal;
Vector reflected;
Position impact;
std::pair<std::shared_ptr<SceneObj>, double> reflectedObj;
normal = math.calcNormalVector(camera.pos, obj, rayVec, k, impact);
view = Vector(impact.x, impact.y, impact.z) -
Vector(camera.pos.x, camera.pos.y, camera.pos.z);
reflected = math.calcReflectedVector(view, normal);
reflectedObj = this->getClosestObj(reflected, Camera(impact));
if (reflectedObj.second <= 0) {
cuColor.mix(0x000000, obj->getReflectionIndex());
return cuColor;
}
newColor = this->getReflectedColor(reflectedObj.first, Camera(impact),
reflected, reflectedObj.second, pass + 1);
// apply effects
cuColor.mix(newColor, obj->getReflectionIndex());
return newColor;
}
To calculate the normal and the reflected Vector:
Vector Math::calcReflectedVector(const Vector &ray,
const Vector &normal) const {
double cosAngle;
Vector copyRay = ray;
copyRay.makeUnit();
cosAngle = copyRay.scale(normal);
return (-2.0 * cosAngle * normal + copyRay);
}
Vector Math::calcNormalVector(Position pos, std::shared_ptr<SceneObj> obj,
Vector rayVec, double k, Position& impact) const {
const Position &objPos = obj->getPosition();
Vector normal;
impact.x = pos.x + k * rayVec.x;
impact.y = pos.y + k * rayVec.y;
impact.z = pos.z + k * rayVec.z;
obj->calcNormal(normal, impact);
return normal;
}
[EDIT1]
I have a new image, i removed the plane only to keep the spheres:
As you can see there is blue and yellow on the border of the sphere.
Thanks to neam I colored the sphere applying the following formula:
newColor.r = reflected.x * 127.0 + 127.0;
newColor.g = reflected.y * 127.0 + 127.0;
newColor.b = reflected.z * 127.0 + 127.0;
Bellow is the visual result:
Ask me if you need any information.
Thanks in advance
There are many little things with the example you provided. This may -- or may not -- answer your question, but as I suppose you're doing a raytracer for learning purposes (either at school or in your free time) I'll give you some hints.
you have two classes Vector and Position. It may well seems like it's a good idea, but why not seeing the position as the translation vector from the origin ? This would avoid some code duplication I think (except if you've done something like using Position = Vector;). You may also want to look at some libraries that does all the mathematical things for you (like glm could do). (and this way, you'll avoid some errors like naming your dot function scale())
you create a camera from the position (that is a really strange thing). Reflections doesn't involve any camera. In a typical raytracer, you have one camera {position + direction + fov + ...} and for each pixels of your image/reflections/refractions/..., you cast rays {origin + direction} (thus the name raytracer, which isn't cameratracer). The Camera class is usually tied to the concept of physical camera with things like focal, depth of field, aperture, chromatic aberration, ... whereas the ray is simply... a ray. (could be a ray from the plane where the output image is mapped to the first object, or a ray created from reflection, diffraction, scattering, ...).
and for the final point, I think that your error may comes from the Math::calcNormalVector(...) function. For a sphere at a position P and for an intersection point I, the normal N is: N = normalize(I - P);.
EDIT: seems like your problem comes from the Rt::getClosestObj. Everything else is looking fine
There's ton a websites/blogs/educative content online about creating a simple raytracer, so for the first two points I let them teach you. Take a look at glm.
If don't figure out what is wrong with calcNormalVector(...) please post its code :)
Did that works ?
I assume that your ray and normal vector are already normalized.
Vector Math::reflect(const Vector &ray, const Vector &normal) const
{
return ray - 2.0 * Math::dot(normal, ray) * normal;
}
Moreover, I can't understand with your provided code this call :
this->getClosestObj(reflected, Camera(obj->getPosition()));
That should be something like that no ?
this->getClosestObj(reflected, Camera(impact));
I am trying to implement a simple raycasting volume rendering in WebGL.
It is kind of working, but there are some artifacts when you rotate the volume around (i.e. the head appears deformed).
Live demo:
http://fnndsc.github.io/vjs/#shaders_raycasting_adibrain
GLSL Code used for debugging:
https://github.com/FNNDSC/vjs/blob/master/src/shaders/shaders.raycasting.secondPass.frag
Simplified version of the code:
for(int rayStep = 0; rayStep < maxSteps; rayStep++){
// map world coordinates to data coordinates
vec4 dataCoordinatesRaw = uWorldToData * currentPosition;
ivec3 dataCoordinates = ivec3(int(floor(dataCoordinatesRaw.x)), int(floor(dataCoordinatesRaw.y)), int(floor(dataCoordinatesRaw.z)));
float intensity = getIntensity(dataCoordinates);
// we have the intensity now
vec3 colorSample = vec3(intensity);
float alphaSample = intensity;
accumulatedColor += (1.0 - accumulatedAlpha) * colorSample * alphaSample;
accumulatedAlpha += alphaSample;
//Advance the ray.
currentPosition += deltaDirection;
accumulatedLength += deltaDirectionLength;
if(accumulatedLength >= rayLength || accumulatedAlpha >= 1.0 ) break;
}
I do not understand what could explain those artifacts.
Could it be because I do not use gradients to modulate opacity/color?
Any hint would be very welcome.
The backface coordinates were not computed properly during the first pass of the raycasting. The range of the "normalized" coodinates was not [0, 1]. It was [-.5, 1.5], therefore creating the visualization artifact as all values outside of [0, 1] range were clamped out.
I've been trying to implement the marching cubes algorithm with C++ and Qt. Anyway, so far all the steps have been written, but I'm getting a really bad result. I'm looking for orientation or advices about what can be going wrong. I suspect one of the problems may be with the voxel conception, specifically about which vertex goes in which corner (0, 1, ..., 7). Also, I'm not a 100% sure about how to interpret the input for the algorithm (I'm using datasets). Should I read it in the ZYX order and move the marching cube in the same way or it doesn't matter at all? (Leaving aside the fact that no every dimension has to have the same size).
Here is what I'm getting against what it should look like...
http://i57.tinypic.com/2nb7g46.jpg
http://en.wikipedia.org/wiki/Marching_cubes
http://en.wikipedia.org/wiki/Marching_cubes#External_links
Paul Bourke. "Overview and source code".
http://paulbourke.net/geometry/polygonise/
Qt_MARCHING_CUBES.zip: Qt/OpenGL example courtesy Dr. Klaus Miltenberger.
http://paulbourke.net/geometry/polygonise/Qt_MARCHING_CUBES.zip
The example requires boost, but looks like it probably should work.
In his example, it has in marchingcubes.cpp, a few different methods for calculating the marching cubes: vMarchCube1 and vMarchCube2.
In the comments it says vMarchCube2 performs the Marching Tetrahedrons algorithm on a single cube by making six calls to vMarchTetrahedron.
Below is the source for the first one vMarchCube1:
//vMarchCube1 performs the Marching Cubes algorithm on a single cube
GLvoid GL_Widget::vMarchCube1(const GLfloat &fX, const GLfloat &fY, const GLfloat &fZ, const GLfloat &fScale, const GLfloat &fTv)
{
GLint iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags;
GLfloat fOffset;
GLvector sColor;
GLfloat afCubeValue[8];
GLvector asEdgeVertex[12];
GLvector asEdgeNorm[12];
//Make a local copy of the values at the cube's corners
for(iVertex = 0; iVertex < 8; iVertex++)
{
afCubeValue[iVertex] = (this->*fSample)(fX + a2fVertexOffset[iVertex][0]*fScale,fY + a2fVertexOffset[iVertex][1]*fScale,fZ + a2fVertexOffset[iVertex][2]*fScale);
}
//Find which vertices are inside of the surface and which are outside
iFlagIndex = 0;
for(iVertexTest = 0; iVertexTest < 8; iVertexTest++)
{
if(afCubeValue[iVertexTest] <= fTv) iFlagIndex |= 1<<iVertexTest;
}
//Find which edges are intersected by the surface
iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];
//If the cube is entirely inside or outside of the surface, then there will be no intersections
if(iEdgeFlags == 0)
{
return;
}
//Find the point of intersection of the surface with each edge
//Then find the normal to the surface at those points
for(iEdge = 0; iEdge < 12; iEdge++)
{
//if there is an intersection on this edge
if(iEdgeFlags & (1<<iEdge))
{
fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ],afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTv);
asEdgeVertex[iEdge].fX = fX + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0] + fOffset * a2fEdgeDirection[iEdge][0]) * fScale;
asEdgeVertex[iEdge].fY = fY + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1] + fOffset * a2fEdgeDirection[iEdge][1]) * fScale;
asEdgeVertex[iEdge].fZ = fZ + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2] + fOffset * a2fEdgeDirection[iEdge][2]) * fScale;
vGetNormal(asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ);
}
}
//Draw the triangles that were found. There can be up to five per cube
for(iTriangle = 0; iTriangle < 5; iTriangle++)
{
if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break;
for(iCorner = 0; iCorner < 3; iCorner++)
{
iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];
vGetColor(sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]);
glColor4f(sColor.fX, sColor.fY, sColor.fZ, 0.6);
glNormal3f(asEdgeNorm[iVertex].fX, asEdgeNorm[iVertex].fY, asEdgeNorm[iVertex].fZ);
glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ);
}
}
}
UPDATE: Github working example, tested
https://github.com/peteristhegreat/qt-marching-cubes
Hope that helps.
Finally, I found what was wrong.
I use a VBO indexer class to reduce the ammount of duplicated vertices and make the render faster. This class is implemented with a std::map to find and discard already existing vertices, using a tuple of < vec3, unsigned short >. As you may imagine, a marching cubes algorithm generates structures with thousands if not millions of vertices. The highest number a common unsigned short can hold is 65536, or 2^16. So, when the output geometry had more than that, the map index started to overflow and the result was a mess, since it started to overwrite vertices with the new ones. I just changed my implementation to draw with common VBO and not indexed while I fix my class to support millions of vertices.
The result, with some minor vertex normal issues, speaks for itself:
http://i61.tinypic.com/fep2t3.jpg
I am building a ray tracer and I am able to correctly render diffuse and specular parts of my sphere. When I come to calculate shadows and reflections however I end up with a very pixelated result as shown in the below image:
I can see that the shadow is cast in the correct place and if you zoom in the reflection is also visible but again pixelated. I call this method to determine if a pixel is in shade and it is also called recursively by my reflect ray method to determine the reflected colours.
RGBColour Scene::illumination(Ray incidentRay, Shape *closestShape, RGBColour shapeColour, Ray ray)
{
RGBColour diffuseLight = _backgroundColour;
RGBColour specularLight = _backgroundColour;
double projectionNormalToSource = 0.0;
for (int i = 0; i < _lightSources.size(); i++)
{
Ray shadowRay(incidentRay.Direction(), (_lightSources.at(i).GetPosition() - incidentRay.Direction()).UnitVector());
Vector surfaceNormal = closestShape->SurfaceNormal(incidentRay);
//lambertian shading.
projectionNormalToSource = surfaceNormal.ScalarProduct(shadowRay.Direction());
if (projectionNormalToSource > 0)
{
bool isShadow = false;
std::vector<double> shadowIntersections;
Ray temp(incidentRay.Direction(), (_lightSources.at(i).GetPosition() - incidentRay.Direction()));
for (int j = 0; j < _sceneObjects.size(); j++)
{
shadowIntersections.push_back(_sceneObjects.at(j)->Intersection(temp));
}
//Test each point to see if it is in shadow.
for (int j = 0; j < shadowIntersections.size(); j++)
{
if (shadowIntersections.at(j) != -1)
{
if (shadowIntersections.at(j) > _epsilon && shadowIntersections.at(j) <= temp.Direction().Magnitude() && closestShape != _sceneObjects.at(j))
{
isShadow = true;
}
break;
}
}
if (!isShadow)
{
diffuseLight = diffuseLight + (closestShape->Colour() * projectionNormalToSource * closestShape->DiffuseCoefficient() * _lightSources.at(i).DiffuseIntensity());
specularLight = specularLight + specularReflection(_lightSources.at(i), projectionNormalToSource, closestShape, incidentRay, temp, ray);
}
}
}
return diffuseLight + specularLight;
}
As I am able to correctly render the spheres apart from these aspects I am convinced the problem must lie within this particular method so I have not posted the others. What I think is happening is that where the pixel values retain their initial colour instead of being shaded I must incorrectly be calculating very small values or the other option is that the calculated ray did not intersect, however I do not think the latter option is valid otherwise the same intersection method would return incorrect results elsewhere in the program but as the spheres render correctly (excluding the shading and reflection).
So typically what causes results like this and can you spot any obvious logic errors in my method?
Edit: I have moved my light source in front and I can now see that the shadow appears to be correctly cast for the green sphere and the blue one becomes pixelated. So I think on any subsequent shape iterations something must not be updating correctly.
Edit 2: The first issue has been fixed and the shadows are now not pixelated, the resolution was to move the break statement into the if statement directly preceding it. The issue that the reflections are still pixelated still occurs currently.
Pixelation like this could occur due to numerical instability. An example: Suppose you calculate an intersection point that lies on a curved surface. You then use that point as the origin of a ray (a shadow ray, for example). You would assume that the ray wouldn't intersect that curved surface, but in practice it sometimes can. You could check for this by discarding such self intersections, but that could cause problems if you decide to implement concave shapes. Another approach could be to move the origin of the generated ray along its direction vector by some infinitesimally small amount, so that no unwanted self-intersection occurs.