about Lighting and shadow casting

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

about Lighting and shadow casting

Post by greenya »

Hello all!

It is looks like when i using lighting (POINT light) stencil shadows, every single scene node getting calculating direction of its shadow just once for a render pass. I mean it doesn't calculates the direction for every single polygon of the mesh of the node.

1st question: am i thinking right?

p.s.: because when i using big ("size" not "poly count") model and the POINT light placed near the node --- i getting the result equivalent to DIRECTIONAL light (so all polygons cast shadows in the same direction... ((( maybe getting from the geometrical center of the node))) ).

2nd question: is there the only way to split big node to smaller ones to solve this?
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

nobody can answer?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Maybe post screenshots and code. And also note that depending on the technqiue of stencil shadowing you use there are known limitations in the algorithm itself, when the camera is inside the shadow volume.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

Ok!

Lets write the code:

Code: Select all

#include <irrlicht.h>

using namespace irr;
using namespace core;

#pragma comment(lib, "Irrlicht.lib")

int main()
{
	// irr device and pointers
	IrrlichtDevice *irrDevice = createDevice(video::EDT_DIRECT3D9, dimension2di(640,480), 16, false, true);
	video::IVideoDriver *irrVideo = irrDevice->getVideoDriver();
	scene::ISceneManager *irrScene = irrDevice->getSceneManager();

	// camera
	scene::ICameraSceneNode* cam = irrScene->addCameraSceneNodeFPS();
	cam->setPosition(vector3df(1,250,10));
	cam->setTarget(vector3df(0,0,0));

	// add animated meshe (because addShadowVolumeSceneNode() is not a member of
	// scene::ISceneNode and simple scene::addCubeSceneNode is not acceptable)
	scene::IAnimatedMesh *am1 = irrScene->getMesh("fachwerk12_LOWPOLY_TS.3ds");
	scene::IAnimatedMeshSceneNode *amsn1 = irrScene->addAnimatedMeshSceneNode(am1);
	amsn1->setMaterialFlag(video::EMF_LIGHTING, false);
	amsn1->setScale(vector3df(0.1f,0.1f,0.1f));
	amsn1->addShadowVolumeSceneNode();

	// add some plane
	scene::ISceneNode *c1 = irrScene->addCubeSceneNode(100);
	c1->setScale(vector3df(4,0.01f,4));
	c1->setMaterialFlag(video::EMF_LIGHTING, false);

	// add light and billboard (to be able to see it)
	scene::ILightSceneNode *l1 = irrScene->addLightSceneNode();
	l1->setPosition(vector3df(100,100,100)); // <--- setting light position; set it to anything you like
											 // for testing (do not forget that light Radius is 100 by default,
											 // so do not used very far value or add code to chaged Radius)
	scene::IBillboardSceneNode *bb1 = irrScene->addBillboardSceneNode(l1, dimension2df(40,40));
	bb1->setMaterialTexture(0, irrVideo->getTexture("light_marker.png"));;
	bb1->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL);
	bb1->setMaterialFlag(video::EMF_LIGHTING, false);

	// this is only for sure
	printf("\n\nUsing the light of %s type\n\n\n", l1->getLightData().Type==video::ELT_POINT?"ELT_POINT":"*NOT* ELT_PPONT");

	// show time!
	irrDevice->getCursorControl()->setVisible(false);
	while(irrDevice->run())
	if (irrDevice->isWindowActive())
	{
		irrVideo->beginScene(true, true, video::SColor(0x204060));
		irrScene->drawAll();
		irrVideo->endScene();
	}

	// drop time! :)
	irrDevice->drop();

	return 0;
}
I have made couple of screenshots while investigation. All my comments added with red.

Image

Image

Image

If you will place couple of this houses (couple of meshes), then they will get different shadow direction, but for each single mesh -- it would be -- directional light (not point light). Looks like the vector of the shadow is calculated once for whole mesh and used for every single vertex only moving this vector in space. This is OK for directional light.

The vector of the shadow must be calculated for each single vertex of the mesh (would be great, if it would be possible to specify for certain meshes the dedicated mesh (very low poly) that will be used specially for light volume), something like:

Code: Select all

amsn1->addShadowVolumeSceneNode();
amsn1->overrideShadowVolume(irrScene->getMesh("low-mesh-shadow.3ds")); // <--- this would be cool
P.S.:
you can download this test project from here -- http://filebeam.com/de1dc5077b5ef2c23714d7eb64f8caa1 (200kb with model, texture and light billboard texture).

P.P.S.:
model used in this test was downloaded from here -- http://www.turbosquid.com/FullPreview/I ... /ID/245845 (it is free).
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

Awesome bug report, complete with code, models and well annotated screenshots. I'll move it to the bug reports forum :)
However, I don't know if this is a problem with volume shadows in general, or it's just the way ours are projected.
You can set the mesh to render from though, which should remove some of those spots from shadow.
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

bitplane wrote: You can set the mesh to render from though, which should remove some of those spots from shadow.
Thank you for this hint!
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

Sorry for offtopic,
but should I post this bug on the SF' bug-tracker -- https://sourceforge.net/tracker/?group_ ... tid=540676 ?

Or do not touch anything :?:
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

Yes, please do :)
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

Steel Style
Posts: 168
Joined: Sun Feb 04, 2007 3:30 pm
Location: France

Post by Steel Style »

It's not perfect but solved. So it's my CShadowVolumeSceneNode.cpp

Image
// Copyright (C) 2002-2007 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "CShadowVolumeSceneNode.h"
#include "ISceneManager.h"
#include "IMesh.h"
#include "IVideoDriver.h"
#include "SLight.h"

namespace irr
{
namespace scene
{


//! constructor
CShadowVolumeSceneNode::CShadowVolumeSceneNode(ISceneNode* parent,
ISceneManager* mgr, s32 id,
bool zfailmethod, f32 infinity)
: IShadowVolumeSceneNode(parent, mgr, id), Indices(0), Vertices(0),
Adjacency(0), FaceData(0), UseZFailMethod(zfailmethod),
IndexCountAllocated(0), VertexCountAllocated(0),
IndexCount(0), VertexCount(0), ShadowVolumesUsed(0),
Edges(0), EdgeCount(0), Infinity(infinity)
{
#ifdef _DEBUG
setDebugName("CShadowVolumeSceneNode");
#endif

setAutomaticCulling(scene::EAC_OFF);
}



//! destructor
CShadowVolumeSceneNode::~CShadowVolumeSceneNode()
{
delete [] Edges;

for (u32 i=0; i<ShadowVolumes.size(); ++i)
delete [] ShadowVolumes.vertices;

delete [] Vertices;
delete [] Indices;
delete [] Adjacency;
delete [] FaceData;
}



void CShadowVolumeSceneNode::createShadowVolume(const core::vector3df& light)
{
SShadowVolume* svp = 0;

// builds the shadow volume and adds it to the shadow volume list.

if (ShadowVolumes.size() > (u32)ShadowVolumesUsed)
{
// get the next unused buffer
svp = &ShadowVolumes[ShadowVolumesUsed];
if (svp->size >= IndexCount*5)
svp->count = 0;
else
{
svp->size = IndexCount*5;
svp->count = 0;
delete [] svp->vertices;
svp->vertices = new core::vector3df[svp->size];
}

++ShadowVolumesUsed;
}
else
{
// add a buffer
SShadowVolume tmp;
// lets make a rather large shadowbuffer
tmp.size = IndexCount*5;
tmp.count = 0;
tmp.vertices = new core::vector3df[tmp.size];
ShadowVolumes.push_back(tmp);
svp = &ShadowVolumes[ShadowVolumes.size()-1];
++ShadowVolumesUsed;
}

const s32 faceCount = (s32)(IndexCount / 3);

if (!Edges || faceCount * 6 > EdgeCount)
{
delete [] Edges;
EdgeCount = faceCount * 6;
Edges = new u16[EdgeCount];
}

s32 numEdges = 0;
const core::vector3df ls = light; //1* Infinity; //2 light scaled
//Steel: I added the 1 quote and this one because the light * infinity mean to a direction vector

//if (UseZFailMethod)
// createZFailVolume(faceCount, numEdges, light, svp);
//else
// createZPassVolume(faceCount, numEdges, light, svp, false);

// the createZFailVolume does currently not work 100% correctly,
// so we create createZPassVolume with caps if the zfail method
// is used
createZPassVolume(faceCount, numEdges, light, svp, UseZFailMethod);


for (s32 i=0; i<numEdges; ++i)
{
core::vector3df &v1 = Vertices[Edges[2*i+0]];
core::vector3df &v2 = Vertices[Edges[2*i+1]];
core::vector3df v3((v1 - ls)*Infinity);
core::vector3df v4((v2 - ls)*Infinity);

// Add a quad (two triangles) to the vertex list
if (svp->vertices && svp->count < svp->size-5)
{
svp->vertices[svp->count++] = v1;
svp->vertices[svp->count++] = v2;
svp->vertices[svp->count++] = v3;

svp->vertices[svp->count++] = v2;
svp->vertices[svp->count++] = v4;
svp->vertices[svp->count++] = v3;
}
}
}

void CShadowVolumeSceneNode::createZFailVolume(s32 faceCount, s32& numEdges,
const core::vector3df& light,
SShadowVolume* svp)
{
s32 i;
const core::vector3df ls = light ;

// Check every face if it is front or back facing the light.
for (i=0; i<faceCount; ++i)
{
const u16 wFace0 = Indices[3*i+0];
const u16 wFace1 = Indices[3*i+1];
const u16 wFace2 = Indices[3*i+2];

const core::vector3df v0 = Vertices[wFace0];
const core::vector3df v1 = Vertices[wFace1];
const core::vector3df v2 = Vertices[wFace2];

if (core::triangle3df(v0,v1,v2).isFrontFacing(light))
{
FaceData = false; // it's a back facing face

if (svp->vertices && svp->count < svp->size-5)
{
// add front cap
svp->vertices[svp->count++] = v0;
svp->vertices[svp->count++] = v2;
svp->vertices[svp->count++] = v1;

// add back cap
svp->vertices[svp->count++] = (v0 - ls)*Infinity;
svp->vertices[svp->count++] = (v1 - ls)*Infinity;
svp->vertices[svp->count++] = (v2 - ls)*Infinity;
}
}
else
FaceData = true; // it's a front facing face
}

for(i=0; i<faceCount; ++i)
{
if (FaceData == true)
{
const u16 wFace0 = Indices[3*i+0];
const u16 wFace1 = Indices[3*i+1];
const u16 wFace2 = Indices[3*i+2];

const u16 adj0 = Adjacency[3*i+0];
const u16 adj1 = Adjacency[3*i+1];
const u16 adj2 = Adjacency[3*i+2];

if (adj0 != (u16)-1 && FaceData[adj0] == false)
{
// add edge v0-v1
Edges[2*numEdges+0] = wFace0;
Edges[2*numEdges+1] = wFace1;
++numEdges;
}

if (adj1 != (u16)-1 && FaceData[adj1] == false)
{
// add edge v1-v2
Edges[2*numEdges+0] = wFace1;
Edges[2*numEdges+1] = wFace2;
++numEdges;
}

if (adj2 != (u16)-1 && FaceData[adj2] == false)
{
// add edge v2-v0
Edges[2*numEdges+0] = wFace2;
Edges[2*numEdges+1] = wFace0;
++numEdges;
}
}
}
}

void CShadowVolumeSceneNode::createZPassVolume(s32 faceCount,
s32& numEdges,
core::vector3df light,
SShadowVolume* svp, bool caps)
{
//light *= Infinity; I used to say NOOOOOOO
if (light == core::vector3df(0,0,0))
light = core::vector3df(0.0001f,0.0001f,0.0001f);

for (s32 i=0; i<faceCount; ++i)
{
const u16 wFace0 = Indices[3*i+0];
const u16 wFace1 = Indices[3*i+1];
const u16 wFace2 = Indices[3*i+2];

if (core::triangle3df(Vertices[wFace0],Vertices[wFace1],Vertices[wFace2]).isFrontFacing(light))
{
Edges[2*numEdges+0] = wFace0;
Edges[2*numEdges+1] = wFace1;
++numEdges;

Edges[2*numEdges+0] = wFace1;
Edges[2*numEdges+1] = wFace2;
++numEdges;

Edges[2*numEdges+0] = wFace2;
Edges[2*numEdges+1] = wFace0;
++numEdges;

if (caps && svp->vertices && svp->count < svp->size-5)
{
svp->vertices[svp->count++] = Vertices[wFace0];
svp->vertices[svp->count++] = Vertices[wFace2];
svp->vertices[svp->count++] = Vertices[wFace1];

svp->vertices[svp->count++] = (Vertices[wFace0] - light)*Infinity;
svp->vertices[svp->count++] = (Vertices[wFace1] - light)*Infinity;
svp->vertices[svp->count++] = (Vertices[wFace2] - light)*Infinity;
}
}
}
}

//! sets the mesh from which the shadow volume should be generated.
void CShadowVolumeSceneNode::setMeshToRenderFrom(const IMesh* mesh)
{
ShadowVolumesUsed = 0;

s32 oldIndexCount = IndexCount;
s32 oldVertexCount = VertexCount;

VertexCount = 0;
IndexCount = 0;

if (!mesh)
return;

// calculate total amount of vertices and indices

u32 i;
s32 totalVertices = 0;
s32 totalIndices = 0;
u32 bufcnt = mesh->getMeshBufferCount();
const IMeshBuffer* b;

for (i=0; i<bufcnt; ++i)
{
b = mesh->getMeshBuffer(i);
totalIndices += b->getIndexCount();
totalVertices += b->getVertexCount();
}

// allocate memory if necessary

if (totalVertices > VertexCountAllocated)
{
delete [] Vertices;
Vertices = new core::vector3df[totalVertices];
VertexCountAllocated = totalVertices;
}

if (totalIndices > IndexCountAllocated)
{
delete [] Indices;
Indices = new u16[totalIndices];
IndexCountAllocated = totalIndices;

if (UseZFailMethod)
{
delete [] FaceData;
FaceData = new bool[totalIndices / 3];
}
}

// copy mesh

for (i=0; i<bufcnt; ++i)
{
b = mesh->getMeshBuffer(i);

s32 idxcnt = b->getIndexCount();
s32 vtxnow = VertexCount;

const u16* idxp = b->getIndices();
const u16* idxpend = idxp + idxcnt;

for (; idxp!=idxpend; ++idxp)
Indices[IndexCount++] = *idxp + vtxnow;

s32 vtxcnt = b->getVertexCount();

switch(b->getVertexType())
{
case video::EVT_STANDARD:
{
const video::S3DVertex* vp = (video::S3DVertex*)b->getVertices();
const video::S3DVertex* const vpend = vp + vtxcnt;

for (; vp!=vpend; ++vp)
Vertices[VertexCount++] = (*vp).Pos;
}
break;
case video::EVT_2TCOORDS:
{
const video::S3DVertex2TCoords* vp = (video::S3DVertex2TCoords*)b->getVertices();
const video::S3DVertex2TCoords* const vpend = vp + vtxcnt;

for (; vp!=vpend; ++vp)
Vertices[VertexCount++] = (*vp).Pos;
}
break;
case video::EVT_TANGENTS:
{
const video::S3DVertexTangents* vp = (video::S3DVertexTangents*)b->getVertices();
const video::S3DVertexTangents* const vpend = vp + vtxcnt;

for (; vp!=vpend; ++vp)
Vertices[VertexCount++] = (*vp).Pos;
}
break;
}
}

// recalculate adjacency if necessary
if (oldVertexCount != VertexCount &&
oldIndexCount != IndexCount && UseZFailMethod)
calculateAdjacency();

// create as much shadow volumes as there are lights but
// do not ignore the max light settings.

const u32 lights = SceneManager->getVideoDriver()->getDynamicLightCount();
core::matrix4 mat = Parent->getAbsoluteTransformation();
const core::vector3df parentpos = Parent->getAbsolutePosition();
core::vector3df lpos;
mat.makeInverse();

// TODO: Only correct for point lights.
for (i=0; i<lights; ++i)
{
const video::SLight& dl = SceneManager->getVideoDriver()->getDynamicLight(i);
lpos = dl.Position;
if (dl.CastShadows &&
fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f))
{
mat.transformVect(lpos);
createShadowVolume(lpos);
}
}
}



//! pre render method
void CShadowVolumeSceneNode::OnRegisterSceneNode()
{
if (IsVisible)
{
SceneManager->registerNodeForRendering(this, scene::ESNRP_SHADOW);
ISceneNode::OnRegisterSceneNode();
}
}



//! renders the node.
void CShadowVolumeSceneNode::render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();

if (!ShadowVolumesUsed || !driver)
return;

driver->setTransform(video::ETS_WORLD, Parent->getAbsoluteTransformation());

for (s32 i=0; i<ShadowVolumesUsed; ++i)
driver->drawStencilShadowVolume(ShadowVolumes.vertices,
ShadowVolumes.count, UseZFailMethod);
}



//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& CShadowVolumeSceneNode::getBoundingBox() const
{
return Box;
}


//! Generates adjacency information based on mesh indices.
void CShadowVolumeSceneNode::calculateAdjacency(f32 epsilon)
{
delete [] Adjacency;
Adjacency = new u16[IndexCount];

epsilon *= epsilon;

f32 t = 0;

// go through all faces and fetch their three neighbours
for (s32 f=0; f<IndexCount; f+=3)
{
for (s32 edge = 0; edge<3; ++edge)
{
core::vector3df v1 = Vertices[Indices[f+edge]];
core::vector3df v2 = Vertices[Indices[f+((edge+1)%3)]];

// now we search an_O_ther _F_ace with these two
// vertices, which is not the current face.

s32 of;

for (of=0; of<IndexCount; of+=3)
if (of != f)
{
s32 cnt1 = 0;
s32 cnt2 = 0;

for (s32 e=0; e<3; ++e)
{
t = v1.getDistanceFromSQ(Vertices[Indices[of+e]]);
if (core::iszero(t))
++cnt1;

t = v2.getDistanceFromSQ(Vertices[Indices[of+e]]);
if (core::iszero(t))
++cnt2;
}

if (cnt1 == 1 && cnt2 == 1)
break;
}

if (of == IndexCount)
Adjacency[f + edge] = f;
else
Adjacency[f + edge] = of / 3;
}
}
}


} // end namespace scene
} // end namespace irr


Last edited by Steel Style on Tue Feb 12, 2008 12:19 pm, edited 1 time in total.
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Wow nice work Steel, now can anyone get rid of the annoying white speckles? :P
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

Steel Style,
good job!

p.s.: waiting to have it after some svn-update...
BlindSide wrote:now can anyone get rid of the annoying white speckles? :P
That would be great!
but, i just want to share my experience: i have to computers and able to check on both, so on my Radeon 200M i am getting the result as you can see on the screenshots above, BUT the same EXE file produces perfect volume shadow on GeForce 7600GS.

p.s.: ati != geforce
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

I have not only some speckles, but also a second complete inverted (i.e. white) house shadow from many angles. So it's sometimes working ok, but when moving around the house there are major artifacts in the shadow. Anyone else? Maybe it's just a more apparent version of the tiny inverted objects which were also in the previous version of the shadow.
Also, the capping of the bottom faces of the objects seems to be broken. I have a completely lit area under the objects, which also extends further outside the objects when the light moves.
Steel Style
Posts: 168
Joined: Sun Feb 04, 2007 3:30 pm
Location: France

Post by Steel Style »

I didn't test Zfail method but as I see now that true, that should be a bug on the Zfail implementation as we see nico use to write:

Code: Select all

// the createZFailVolume does currently not work 100% correctly,
// so we create createZPassVolume with caps if the zfail method
// is used
I hope that I will have the time to look later.
Steel Style
Posts: 168
Joined: Sun Feb 04, 2007 3:30 pm
Location: France

Post by Steel Style »

I think it's work now, please try and submit the result here thanks.

http://steelstyle.free.fr/IrrImg/ShadowVolumes01.patch
Post Reply