IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenGL Discussion :

Problème d'implémentation parallel split shadow mapping


Sujet :

OpenGL

  1. #1
    Membre averti
    Profil pro
    Responsable technique
    Inscrit en
    Février 2006
    Messages
    363
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Responsable technique

    Informations forums :
    Inscription : Février 2006
    Messages : 363
    Points : 353
    Points
    353
    Par défaut Problème d'implémentation parallel split shadow mapping
    Bonjour à tous,

    Je suis en train d'implémenter un algo de parallel split shadow mapping. Tout fonctionne presque bien, sauf que... Sauf que le frustrum de ma lumière change en fonction de la position et l'orientation de ma caméra.

    Voici des screenshot qui illustrent mon problème:

    Premier cas, je me positionne à un endroit quelconque de ma scène:



    Vous ne pouvez pas le voir mais deja le frustrum de la lumière n'est pas bon.

    A partir de cette position, j'effectue un déplacement latéral vers la droite sans changer l'orientation de ma caméra et voici ce que j'obtient:



    Le 2e screenshot montre clairement que le frustrum de la lumière a changé.

    Second cas, je me positionne en dessous de la lumière:



    Le frustrum de la lumière semble nul, cad que chancun des point du frustrum ont la meme position. Et voici ce que j'obtient lorsque j'effectue un déplacement vers l'arrière et que je ne suis plus sous la lumière:



    Tout est correct.

    Troisième cas, pour montrer cette "annulation du frustrum de la lumière":

    Je me positionne à un endroit de ma scène:



    Ensuite, je ne change pas de position, mais je fais varier l'orientation de la caméra par un "mouvement de tete" vers la droite:



    Le frustrum (la partie encore claire) s'est considérablement reduit.

    Je continue mon mouvement de tete vers la droite et je tombe sur ma fameuse annulation du frustrum:



    Enfin, pour terminer, voila un screenshot ou tout est correct:



    Pour ce qui est des sources, les voici:

    Méthode d'initialisation de la shadow map:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
            glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
     
    	//On cree la texture qui va contenir la shadow map
    	glGenTextures(1, &TextureId);
    	glActiveTextureARB(GL_TEXTURE3_ARB);
    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, TextureId);
     
    	glTexImage2D(	GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadow_map_size, shadow_map_size, 0,
    					GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//LINEAR pour avoir un PCF x2
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//LINEAR pour avoir un PCF x2
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, /*GL_CLAMP*/GL_CLAMP_TO_BORDER);//PB DE PERF POSSIBLE AVEC CLAMP_TO_BORDER
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, /*GL_CLAMP*/GL_CLAMP_TO_BORDER);//PB DE PERF POSSIBLE AVEC CLAMP_TO_BORDER
     
    	glActiveTextureARB(GL_TEXTURE3_ARB);
    	glDisable(GL_TEXTURE_2D);
     
    	//Creation du FBO
    	glGenFramebuffersEXT(1, &FboId);
    	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FboId);
    	glDrawBuffer(GL_NONE);
    	glReadBuffer(GL_NONE);
     
    	//Associe le FBO à la texture de shadow map.
    	//Comme ca quand on ecrit dans le FBO, le résultat se trouve dans la texture
    	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, TextureId, 0);
     
    	GLenum  FBOstatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    	if(FBOstatus != GL_FRAMEBUFFER_COMPLETE_EXT)
    		printf("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");
     
    	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
     
    	//glActiveTextureARB(GL_TEXTURE0_ARB);
     
    	//Use the color as the ambient and diffuse material
    	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    	glEnable(GL_COLOR_MATERIAL);
     
    	//White specular material color, shininess 16
    	glMaterialfv(GL_FRONT, GL_SPECULAR, white);
    	glMaterialf(GL_FRONT, GL_SHININESS, 16.0f);
    La méthode qui calcule chaque split:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    void CalculateSplitDistances(int iNumOfSplits, float fNear, float fFar)
    {
     
    	// The lambda must me between 0.0f and 1.0f
    	float fLambda = m_fSplitLambda;
     
    	for(int i = 0; i < iNumOfSplits; i++)
    	{
    		float fIDM = i / (float)iNumOfSplits;
    		float fLog = fNear * powf((fFar/fNear), fIDM);
    		float fUniform = fNear + (fFar - fNear)*fIDM;
    		m_fSplitDistances[i] = fLog * fLambda + fUniform*(1-fLambda);
    	}
     
    	// This is used to improve the correctness of the calculations. Our main near- and farplane
    	// of the camera always stay the same, no matter what happens.
    	m_fSplitDistances[0] = fNear;
    	m_fSplitDistances[iNumOfSplits] = fFar;
    }
    La méthode qui calcule le frustrum de la caméra:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    void CalculateFrustumCorners(GSEPOINT3D pPoints[8], GSEPOINT3D vPos, GSEPOINT3D vView,
    									   GSEPOINT3D vUp, float fNear, float fFar, float fScale,
    									   float fFOV, float fAspect)
    {
    	GSEPOINT3D vZ= vView - vPos;
    	vZ.Normalize();
     
    	GSEPOINT3D vX;
    	vX.CrossProduct(vUp, vZ);
    	vX.Normalize();
     
    	GSEPOINT3D vY;
    	vY.CrossProduct(vZ, vX);
     
    	float fNearPlaneHeight = tanf(GSEGET_RADIANS(fFOV) * 0.5f) * fNear;
    	float fNearPlaneWidth = fNearPlaneHeight * fAspect;
     
    	float fFarPlaneHeight = tanf(GSEGET_RADIANS(fFOV) * 0.5f) * fFar;
    	float fFarPlaneWidth = fFarPlaneHeight * fAspect;
     
    	vZ *= fNear;
    	GSEPOINT3D vNearPlaneCenter = vPos + vZ;
    	vZ *= fFar;
    	GSEPOINT3D vFarPlaneCenter = vPos + vZ;
     
    	pPoints[0] = GSEPOINT3D(vNearPlaneCenter - (vX*fNearPlaneWidth) - (vY*fNearPlaneHeight));
    	pPoints[1] = GSEPOINT3D(vNearPlaneCenter - (vX*fNearPlaneWidth) + (vY*fNearPlaneHeight));
    	pPoints[2] = GSEPOINT3D(vNearPlaneCenter + (vX*fNearPlaneWidth) + (vY*fNearPlaneHeight));
    	pPoints[3] = GSEPOINT3D(vNearPlaneCenter + (vX*fNearPlaneWidth) - (vY*fNearPlaneHeight));
     
    	pPoints[4] = GSEPOINT3D(vFarPlaneCenter - (vX*fFarPlaneWidth) - (vY*fFarPlaneHeight));
    	pPoints[5] = GSEPOINT3D(vFarPlaneCenter - (vX*fFarPlaneWidth) + (vY*fFarPlaneHeight));
    	pPoints[6] = GSEPOINT3D(vFarPlaneCenter + (vX*fFarPlaneWidth) + (vY*fFarPlaneHeight));
    	pPoints[7] = GSEPOINT3D(vFarPlaneCenter + (vX*fFarPlaneWidth) - (vY*fFarPlaneHeight));
     
    	GSEPOINT3D vCenter(0.0f, 0.0f, 0.0f);
    	for(int i = 0; i < 8; i++) 
    		vCenter = vCenter + pPoints[i];
     
    	vCenter /= 8.0f;
     
    	for(int i = 0; i < 8; i++) 
    		pPoints[i] = pPoints[i] + ((pPoints[i]-vCenter)*(fScale-1.0f));
    }
    Celle qui calcule la crop matrix:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
     
    //pFrustum est le frustrum de la caméra calculé dans 
    // CalculateFrustumCorners (nom du paramètre du frustrum:pPoints)
    void CalculateLightForFrustum(GSEPOINT3D pFrustum[8])
    {
     
    	// Get the light projection matrix, without altering the current one
    	// !!!ATTENTION!!!
    	// We use 45 degrees as the light's FOV in OpenGL. Other values produce erronous results
    	// from time to time. Anyway, it has the advantage of improving precision, so there's no
    	// reason not to use it.
    	// We chose a small value for the initial farplane to keep precision high in the early
    	// calculations. It doesn't make a big difference, but everything is welcome.
    	// !!!ATTENTION!!!
    	// Remember, the depthtexture uses an aspect ratio of 1.0f (e.g. 2048x2048)
    	glMatrixMode(GL_PROJECTION);
    	glPushMatrix();
    	glLoadIdentity();
    	gluPerspective(45.0f, 1.0f, 1.0f, 50.0f);
    	glGetFloatv(GL_PROJECTION_MATRIX, m_lightProjection);
    	glPopMatrix();
     
    	// Get the light modelview matrix, without altering the current one
    	glMatrixMode( GL_MODELVIEW );
    	glPushMatrix();
            glLoadIdentity();
    	// We render from the light's position, looking at the origin with the y-axis as upvector
    	GSEPOINT3D LightLookAt=LightPosition+LightDirection;
            gluLookAt(LightPosition.x, LightPosition.y, LightPosition.z,
                  LightLookAt.x, LightLookAt.y, LightLookAt.z,
                  LightUpVector.x, LightUpVector.y, LightUpVector.z );
    	glGetFloatv(GL_MODELVIEW_MATRIX, m_lightLookAt);
    	glPopMatrix();
     
    	// Next we will find the min and max values of the current frustum split in the lights
        // post-projection space (where coordinate range is from -1.0f to 1.0f)
    	// !!!ATTENTION!!!
    	// In OpenGL the Z coordinates in post projection space range from [-1,1], 
    	// in Direct3D they range from [0,1]
    	float fMaxX = -1.0f;
            float fMaxY = -1.0f;
            float fMinX =  1.0f;
            float fMinY =  1.0f;
    	float fMaxZ =  -1.0f;
     
    	// We multiply the current projection matrix with the modelview matrix of the light
    	// !!!ATTENTION!!!
    	// In Direct3D, the order of multiplication would be different:
    	// --> CMatrix4x4 lightviewproj = m_LookAtMatrix * m_lightProjection;
    	// The OpenGL FAQ says the following: "Note that post-multiplying with column-major 
    	// matrices produces the same result as pre-multiplying with row-major matrices".
    	GSEMATRIX4X4 lightviewproj = m_lightProjection * m_lightLookAt;
     
    	// We transform the camera's frustum into post-projection space and get the
    	// min and max values for X,Y and Z.
    	// The X and Y values are used to create a zoom-in matrix and the farthest Z value
    	// is used as the new farplane for the light to improve precision.
    	for(int i = 0; i < 8; i++)
    	{
    		// We transform the camera's frustum into post-projection space by multiplying it's
    		// vertices with the lightviewproj matrix.
    		// !!!ATTENTION!!!
    		// The w-coordinate of the frustum vertices MUST be set to 1.0f!!!
    		// I do this in my CVector4() contructor.
    		GSEVECTOR4D vTransformed;
    		vTransformed.w=1.0;
    		vTransformed = lightviewproj.VectorMatrixMultiply(GSEVECTOR4D(pFrustum[i]));
     
    		// We project the x and y values prior to determining the max values
    		vTransformed.x /= vTransformed.w;
    		vTransformed.y /= vTransformed.w;
     
    		// We find the min and max values for X, Y and Z
    		if(vTransformed.x > fMaxX) fMaxX = vTransformed.x;
    		if(vTransformed.y > fMaxY) fMaxY = vTransformed.y;
    		if(vTransformed.y < fMinY) fMinY = vTransformed.y;
    		if(vTransformed.x < fMinX) fMinX = vTransformed.x;
    		if(vTransformed.z > fMaxZ) fMaxZ = vTransformed.z;
    	}
     
    	// We clamp the max X and Y values (post projection space: X and Y between [-1,1])
    	fMaxX = Clamp(fMaxX, -1.0f, 1.0f);
            fMaxY = Clamp(fMaxY, -1.0f, 1.0f);
            fMinX = Clamp(fMinX, -1.0f, 1.0f);
            fMinY = Clamp(fMinY, -1.0f, 1.0f);
     
    	// We use the max Z-value + the light's nearplane + some bias as the new farplane
    	float fLightFar = fMaxZ + 1.0f + 1.5f;
     
    	// We set a new projection matrix for the light with the new farplane
    	// !!!ATTENTION!!!
    	// Most of the time, we're able to set the nearplane a little bit further to improve
    	// our precision even more.
    	// Remember, the depthtexture uses an aspect ratio of 1.0f (e.g. 2048x2048)
    	// Again we use a FOV of 45 degrees with OpenGL, as it improves precision and doesn't
    	// give erronous results.
    	glMatrixMode( GL_PROJECTION );
    	glPushMatrix();
    	glLoadIdentity();
    	gluPerspective(45.0f, 1.0f, 1.0, fLightFar);	
    	glGetFloatv(GL_PROJECTION_MATRIX, m_lightProjection);
    	glPopMatrix();
     
    	// We make an appropriate matrix for zooming in on the current split, just as in the paper
    	float fScaleX = 2.0f / (fMaxX-fMinX);
    	float fScaleY = 2.0f / (fMaxY-fMinY);
    	float fOffsetX = -0.5f * (fMaxX+fMinX) * fScaleX;
    	float fOffsetY = -0.5f * (fMaxY+fMinY) * fScaleY;
     
    	// We set the matrix to the identity and fill in the important values
    	GSEMATRIX4X4 CropView;
    	CropView.LoadIdentity();
     
    	// !!!ATTENTION!!!
    	// This does not need to be the transpose matrix of the Direct3D version!
    	CropView.entries[0] = fScaleX;
    	CropView.entries[5] = fScaleY;
    	CropView.entries[12] = fOffsetX;
    	CropView.entries[13] = fOffsetY;
     
    	// !!!ATTENTION!!!
    	// Due to row-major and column major notation of matrices, in Direct3D the multiplication
    	// order would be different: --> m_lightProjection = m_lightProjection * CropView;
    	// The OpenGL FAQ says the following: "Note that post-multiplying with column-major 
    	// matrices produces the same result as pre-multiplying with row-major matrices".
    	m_lightProjection = CropView * m_lightProjection;
    }
    Pour finir, la boucle de jeu:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
     
            Camera->GetKeyboardControl();
    	Camera->GetMouseControl();
    	Camera->Update();
     
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     
    	float fCameraFar =GSEApplication->GetFar();
     
    	CalculateSplitDistances(iNumOfSplits, 1.0f, fCameraFar);
     
    	for(int iSplit = 0; iSplit < iNumOfSplits; iSplit++)
    	{
    		float fNear =m_fSplitDistances[iSplit];
    		float fFar = m_fSplitDistances[iSplit+1];
     
    		GSEPOINT3D pFrustum[8];
    		float iWidth, iHeight;
    		iWidth = GSEApplication->GetSizeX();
    		iHeight = GSEApplication->GetSizeY();
    		float fAspect = iWidth / iHeight;
    		CalculateFrustumCorners(pFrustum, GSECamera->GetPosition(), GSECamera->GetTarget(),
    			GSECamera->GetUpVector(), GSEApplication->GetNear(), fFar, 1.0f, GSEApplication->GetFOV(), fAspect);
     
    		CalculateLightForFrustum(pFrustum);
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,FboId);
    		glViewport(0, 0, shadow_map_size, shadow_map_size);
    		glDepthRange(0.0f, 1.0f);
     
    		// We only have to clear the depth buffer, since we're only interested in the depth
    		glClear(GL_DEPTH_BUFFER_BIT);
     
    		// Disable all superfluous states to enable fast Z-only rendering on new graphics cards
    		glDisable(GL_LIGHTING);
    		glDisable(GL_TEXTURE_2D);
    		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    		glDisable(GL_ALPHA_TEST);
     
    		// We use some offset to hide ugly selfshadowing artifacts
    		// Unfortunately, my geometry isn't closed or convex, so I can't render only backfaces
    		// into the depthtexture
    		// !!! ATTENTION !!!
    		// Bear in mind that more bias causes the shadows to pop with PSSMs. See for yourself
    		// what I mean by altering these values.
    		glPolygonOffset( 2.0f, 8.0f );
    		glEnable( GL_POLYGON_OFFSET_FILL );
     
    		// We keep depth writes enabled
    		glDepthMask(1);
    		glDepthFunc(GL_LESS);
     
    		// Use the appropriate zoomed-in matrix for the current split
    		glMatrixMode(GL_PROJECTION);
    		glLoadMatrixf(m_lightProjection);
     
    		// Use the appropriate modelview matrix for the current split
    		glMatrixMode(GL_MODELVIEW);
    		glLoadMatrixf(m_lightLookAt);
     
    		// Enable culling of the backfaces and render the scene geometry to the depthtexture
    		glEnable(GL_CULL_FACE);
    		DrawScene();
     
    		// restore all the states again for regular drawing
    		glDepthFunc(GL_LEQUAL);
    		glEnable(GL_LIGHTING);
    		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    		glEnable(GL_TEXTURE_2D);
    		glDisable( GL_POLYGON_OFFSET_FILL );
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    		glViewport(0, 0, GSEApplication->GetSizeX(), GSEApplication->GetSizeY());
    		glDepthRange(iSplit/(float)iNumOfSplits, (iSplit+1)/(float)iNumOfSplits);
     
    		glMatrixMode(GL_PROJECTION);
    		glLoadIdentity();
    		gluPerspective(GSEApplication->GetFOV(), fAspect, fNear, fFar);
     
    		glMatrixMode(GL_MODELVIEW);	
    		glMatrixMode(GL_PROJECTION);
    		glGetFloatv(GL_PROJECTION_MATRIX, m_ProjMatrixCamera);
     
    		glMatrixMode(GL_MODELVIEW);
    		glLoadIdentity();
    		GSEPOINT3D CameraPosition=GSECamera->GetPosition();
    		GSEPOINT3D CameraTarget=GSECamera->GetTarget();
    		GSEPOINT3D CameraUpVector=GSECamera->GetUpVector();
    		gluLookAt(CameraPosition.x, CameraPosition.y, CameraPosition.z,
    			  CameraTarget.x,	 CameraTarget.y,		CameraTarget.z,
    			  CameraUpVector.x, CameraUpVector.y,	CameraUpVector.z);
     
    		glGetFloatv(GL_MODELVIEW_MATRIX, m_LookAtMatrixCamera);
     
    		//tmpPos sert juste à faire une conversion rapide entre un GSEPOINT3D et un GLfloat
    		GLfloat tmpPos[4]={LightPosition.x,LightPosition.y,LightPosition.z,1.0};
    		//tmpWhite permet de définir la noirceur de l'ombre
    		GLfloat tmpWhite[4]={white[0]*Shadowness,white[1]*Shadowness,white[2]*Shadowness,white[3]*Shadowness};
     
    		glLightfv(GL_LIGHT1, GL_POSITION, tmpPos);
    		glLightfv(GL_LIGHT1, GL_AMBIENT, tmpWhite);
    		glLightfv(GL_LIGHT1, GL_DIFFUSE, tmpWhite);
    		glLightfv(GL_LIGHT1, GL_SPECULAR, black);
     
    		glEnable(GL_LIGHT1);
    		glEnable(GL_LIGHTING);
     
    		glActiveTextureARB(GL_TEXTURE3_ARB);
    		glEnable(GL_TEXTURE_2D);
     
    		float x[] = { 1.0f, 0.0f, 0.0f, 0.0f };
    		float y[] = { 0.0f, 1.0f, 0.0f, 0.0f };
    		float z[] = { 0.0f, 0.0f, 1.0f, 0.0f };
    		float w[] = { 0.0f, 0.0f, 0.0f, 1.0f };
     
    		const GLfloat bias[] = {0.5f, 0.0f, 0.0f, 0.0f, 
    				 			 0.0f, 0.5f, 0.0f, 0.0f,
    							 0.0f, 0.0f, 0.5f, 0.0f,
    							 0.5f, 0.5f, 0.5f, 1.0f};
     
    		glTexGenfv( GL_S, GL_EYE_PLANE, x );
    		glTexGenfv( GL_T, GL_EYE_PLANE, y );
    		glTexGenfv( GL_R, GL_EYE_PLANE, z );
    		glTexGenfv( GL_Q, GL_EYE_PLANE, w );
     
    		glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
     
    		glEnable( GL_TEXTURE_GEN_S );
    		glEnable( GL_TEXTURE_GEN_T );
    		glEnable( GL_TEXTURE_GEN_R );
    		glEnable( GL_TEXTURE_GEN_Q );
     
    		glMatrixMode(GL_TEXTURE);
    		glLoadMatrixf(bias);
    		glMultMatrixf(m_lightProjection);
    		glMultMatrixf(m_lightLookAt);
    		glMatrixMode(GL_MODELVIEW);
     
    		DrawScene();
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
     
    		glDisable( GL_TEXTURE_2D );
    		glDisable( GL_TEXTURE_GEN_S );
    		glDisable( GL_TEXTURE_GEN_T );
    		glDisable( GL_TEXTURE_GEN_R );
    		glDisable( GL_TEXTURE_GEN_Q );
    	}
    Il est très probable que mon problème se situe dans la méthode de calcul du frustrum de la lumière mais je ne vois pas ou donc je me permets d'abuser de votre savoir :p.

    Merci.

    PS: Je me suis basé sur un document de nvidia pour ce code:
    http://http.developer.nvidia.com/GPU...ems3_ch10.html

  2. #2
    Membre averti
    Profil pro
    Responsable technique
    Inscrit en
    Février 2006
    Messages
    363
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Responsable technique

    Informations forums :
    Inscription : Février 2006
    Messages : 363
    Points : 353
    Points
    353
    Par défaut
    En fait, il semble que mon problème vienne de la CropMatrix car quand je retire la dernière de ma fonction CalculateLightForFrustum à savoir "m_lightProjection = CropView * m_lightProjection;", tout fonctionne bien. Ce qui est logique car ma m_lightProjection à pour valeur la valeur calculé par gluPerspective(45.0f, 1.0f, 1.0, fLightFar);. Bon alors en soit tant que ca marche ca me va. Mais parmi tous les documents que j'ai vu sur PSSM ou le CSM, il y a avait du texte sur la CropMatrix et quelquepart ca me gène de ne pas l'utiliser. Quelqu'un peut il donc me dire à quoi sert cette matrice car il semble que je n'ai pas compris cela? Et que se passe t'il si je décide de zapper ce calcul?

    Sinon tous les cas qui fonctionnaient lors de mon post précedent, était due au fait que la CropMatrix valait la matrice identité. Donc logique que ca fonctionne.

  3. #3
    Membre averti
    Profil pro
    Responsable technique
    Inscrit en
    Février 2006
    Messages
    363
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Responsable technique

    Informations forums :
    Inscription : Février 2006
    Messages : 363
    Points : 353
    Points
    353
    Par défaut
    Bon alors à priori j'ai réussi. L'algorithme est assez sensible vis-à-vis du Far plane et de la bounding box de la scène.

    Bon sinon, maintenant j'ai un soucis lors de la projection de la shadow map.

    Enfait lorsque je passe par le pipeline fixe tout fonctionne bien:



    Ce qui me gène là dedans, c'est que je dois dessiner la scène 3 fois par split. Avec 3 splits, je dessine donc la scène 9 fois et ça me plait moyen niveau performances.

    Je me suis donc dis que de passer par les shader me permettrait de gagner sur le nombre de fois que je dessine me scène et en plus ça me permettra de vraiment m'y mettre car je n'avais jamais réellement prit le temps de voir comment ça marche.

    Je me suis donc lancé dans le codage d'un shader. J'ai réussi à plaquer mes textures et mes lightmap. Le résultat est le même qu'avec le pipeline fixe.

    Ensuite j'ai voulu plaquer ma shadow map, mais je pense que je n'arrive pas à récupérer les coordonnées de texture correctement ou alors que le rendu final de ma scène est mal placé dans le code.

    J'aimerais donc savoir comment faut il plaquer les shadow map résultant de chaque split en glsl?

    Voila le code de ma boucle de jeu:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
     
     
    	Camera->GetKeyboardControl();
    	Camera->GetMouseControl();
    	Camera->Update();
     
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     
    	float fCameraFar =AdjustCameraPlanes();
     
    	CalculateSplitDistances(iNumOfSplits, 1.0f, fCameraFar);
     
    	for(int iSplit = 0; iSplit < iNumOfSplits; iSplit++)
    	{
    		float fNear =m_fSplitDistances[iSplit];
    		float fFar = m_fSplitDistances[iSplit+1];
     
    		GSEPOINT3D pFrustum[8];
    		float iWidth, iHeight;
    		iWidth = GSEApplication->GetSizeX();
    		iHeight = GSEApplication->GetSizeY();
    		float fAspect = iWidth / iHeight;
    		CalculateFrustumCorners(pFrustum, GSECamera->GetPosition(), GSECamera->GetTarget(),
    			GSECamera->GetUpVector(), GSEApplication->GetNear(), fFar, 1.0f, GSEApplication->GetFOV(), fAspect);
     
    		CalculateLightForFrustum(pFrustum);
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,FboId);
    		glViewport(0, 0, shadow_map_size, shadow_map_size);
    		glDepthRange(0.0f, 1.0f);
     
    		// We only have to clear the depth buffer, since we're only interested in the depth
    		glClear(GL_DEPTH_BUFFER_BIT);
     
    		// Disable all superfluous states to enable fast Z-only rendering on new graphics cards
    		glDisable(GL_LIGHTING);
    		glDisable(GL_TEXTURE_2D);
    		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    		glDisable(GL_ALPHA_TEST);
     
    		// We use some offset to hide ugly selfshadowing artifacts
    		// Unfortunately, my geometry isn't closed or convex, so I can't render only backfaces
    		// into the depthtexture
    		// !!! ATTENTION !!!
    		// Bear in mind that more bias causes the shadows to pop with PSSMs. See for yourself
    		// what I mean by altering these values.
    		glPolygonOffset( 2.0f, 8.0f );
    		glEnable( GL_POLYGON_OFFSET_FILL );
     
    		// We keep depth writes enabled
    		glDepthMask(1);
    		glDepthFunc(GL_LESS);
     
    		// Use the appropriate zoomed-in matrix for the current split
    		glMatrixMode(GL_PROJECTION);
    		glLoadMatrixf(m_lightProjection);
     
    		// Use the appropriate modelview matrix for the current split
    		glMatrixMode(GL_MODELVIEW);
    		glLoadMatrixf(m_lightLookAt);
     
    		// Enable culling of the backfaces and render the scene geometry to the depthtexture
    		glEnable(GL_CULL_FACE);
    		DrawScene(0.1);
     
    		// restore all the states again for regular drawing
    		glDepthFunc(GL_LEQUAL);
    		glEnable(GL_LIGHTING);
    		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    		glEnable(GL_TEXTURE_2D);
    		glDisable( GL_POLYGON_OFFSET_FILL );
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    		glViewport(0, 0, GSEApplication->GetSizeX(), GSEApplication->GetSizeY());
    		glDepthRange(iSplit/(float)iNumOfSplits, (iSplit+1)/(float)iNumOfSplits);
     
    		glMatrixMode(GL_PROJECTION);
    		glLoadIdentity();
    		gluPerspective(GSEApplication->GetFOV(), fAspect, fNear, fFar);
     
    		glMatrixMode(GL_MODELVIEW);	
    		glMatrixMode(GL_PROJECTION);
    		glGetFloatv(GL_PROJECTION_MATRIX, m_ProjMatrixCamera);
     
    		glMatrixMode(GL_MODELVIEW);
    		glLoadIdentity();
    		GSEPOINT3D CameraPosition=GSECamera->GetPosition();
    		GSEPOINT3D CameraTarget=GSECamera->GetTarget();
    		GSEPOINT3D CameraUpVector=GSECamera->GetUpVector();
    		gluLookAt(CameraPosition.x, CameraPosition.y, CameraPosition.z,
    			  CameraTarget.x,	 CameraTarget.y,		CameraTarget.z,
    			  CameraUpVector.x, CameraUpVector.y,	CameraUpVector.z);
     
    		glGetFloatv(GL_MODELVIEW_MATRIX, m_LookAtMatrixCamera);
     
    		//tmpPos sert juste à faire une conversion rapide entre un GSEPOINT3D et un GLfloat
    		GLfloat tmpPos[4]={LightPosition.x,LightPosition.y,LightPosition.z,1.0};
    		//tmpWhite permet de définir la noirceur de l'ombre
    		GLfloat tmpWhite[4]={white[0]*Shadowness,white[1]*Shadowness,white[2]*Shadowness,white[3]*Shadowness};
     
    		glLightfv(GL_LIGHT1, GL_POSITION, tmpPos);
    		glLightfv(GL_LIGHT1, GL_AMBIENT, tmpWhite);
    		glLightfv(GL_LIGHT1, GL_DIFFUSE, tmpWhite);
    		glLightfv(GL_LIGHT1, GL_SPECULAR, black);
     
    		glEnable(GL_LIGHT1);
    		glEnable(GL_LIGHTING);
     
    #ifndef __SHADER__
    		DrawScene(0.0);
    		glLightfv(GL_LIGHT1, GL_DIFFUSE, white);
    	glLightfv(GL_LIGHT1, GL_SPECULAR, white);
    	//Activation du blending pour avoir un mélange des couleurs entres les différentes unités de texture
    	glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ZERO); 
    #endif
     
    		glActiveTextureARB(GL_TEXTURE3_ARB);
    		glBindTexture(GL_TEXTURE_2D,TextureId);
    		glEnable(GL_TEXTURE_2D);
     
    		float x[] = { 1.0f, 0.0f, 0.0f, 0.0f };
    		float y[] = { 0.0f, 1.0f, 0.0f, 0.0f };
    		float z[] = { 0.0f, 0.0f, 1.0f, 0.0f };
    		float w[] = { 0.0f, 0.0f, 0.0f, 1.0f };
     
    		const GLfloat bias[] = {0.5f, 0.0f, 0.0f, 0.0f, 
    				 			 0.0f, 0.5f, 0.0f, 0.0f,
    							 0.0f, 0.0f, 0.5f, 0.0f,
    							 0.5f, 0.5f, 0.5f, 1.0f};
    #ifndef __SHADER__
    		//On effectue un mélange
    	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
     
    	//Entre la shadow map
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB,GL_TEXTURE);
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
     
    	//Et le résultat du précédent mélange à savoir:
    	// Pour les modèles 3D: la texture d'habillage
    	//Pour le décor: la texture d'habillage + le lightmapping
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
    #endif
     
    		glTexGenfv( GL_S, GL_EYE_PLANE, x );
    		glTexGenfv( GL_T, GL_EYE_PLANE, y );
    		glTexGenfv( GL_R, GL_EYE_PLANE, z );
    		glTexGenfv( GL_Q, GL_EYE_PLANE, w );
     
    		glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    		glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
     
    		glEnable( GL_TEXTURE_GEN_S );
    		glEnable( GL_TEXTURE_GEN_T );
    		glEnable( GL_TEXTURE_GEN_R );
    		glEnable( GL_TEXTURE_GEN_Q );
    #ifndef __SHADER__
    		//Active la comparaison des ombres
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
     
    	//Le test de comparaison d'ombrage devrait etre vrai (ie pas dans l'ombre) si r<=texture
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
     
    	//Le test de comparaison d'ombrage génére un resultat d'intensité
    	glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
     
    	glAlphaFunc(GL_GEQUAL, 0.99f);
    	glEnable(GL_ALPHA_TEST);
    #endif
     
    		glMatrixMode(GL_TEXTURE);
    		glLoadMatrixf(bias);
    		glMultMatrixf(m_lightProjection);
    		glMultMatrixf(m_lightLookAt);
    		glMatrixMode(GL_MODELVIEW);
     
    #ifdef __SHADER__
    		Shader->Begin();
    		Shader->sendUniform1i("tex",0);
    		Shader->sendUniform1i("texLightMap",1);
    		Shader->sendUniform1i("texShadowMap",3);
    #endif
    		DrawScene(0.0);
    #ifdef __SHADER__
    		Shader->End();
    #endif
     
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
     
    		glDisable( GL_TEXTURE_2D );
    		glDisable( GL_TEXTURE_GEN_S );
    		glDisable( GL_TEXTURE_GEN_T );
    		glDisable( GL_TEXTURE_GEN_R );
    		glDisable( GL_TEXTURE_GEN_Q );
    #ifndef __SHADER__
    		//Restore other states
    	glDisable(GL_LIGHTING);
    	glDisable(GL_ALPHA_TEST);
    	glDisable(GL_BLEND);
     
    	glActiveTextureARB(GL_TEXTURE3_ARB);
    	glActiveTextureARB(GL_TEXTURE0_ARB);
    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, TextureId);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
    	glDisable(GL_TEXTURE_2D);
    #endif
    Le vertex shader:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    varying vec3 lightDir,normal;
     
    void main()
    {
     
    	normal = normalize(gl_NormalMatrix * gl_Normal);
    	lightDir = normalize(vec3(gl_LightSource[1].position));
     
    	gl_TexCoord[0] = gl_MultiTexCoord0;
    	gl_TexCoord[1] = gl_MultiTexCoord1;
    	gl_TexCoord[3] = gl_MultiTexCoord3;
     
    	gl_Position = ftransform();
    }
    le pixel shader:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    varying vec3 lightDir,normal;
    uniform sampler2D tex,texLightMap,texShadowMap;
     
    void main()
    {
    	vec3 ct,cf;
    	vec4 texel0;
    	vec4 texel1;
    	vec4 texel3;
    	vec4 texelIntermediaire;
    	vec4 texel;
    	float intensity,at,af;
     
    	intensity = max(dot(lightDir,normalize(normal)),0.0);
     
    	cf = intensity * (gl_FrontMaterial.diffuse).rgb + gl_FrontMaterial.ambient.rgb;
    	af = gl_FrontMaterial.diffuse.a;
     
    	texel0 = texture2D(tex,gl_TexCoord[0].st);
    	texel1 = texture2D(texLightMap,gl_TexCoord[1].st);
     
    	if(texel1.rgb==vec3(0.0,0.0,0.0))
    	{
    		texel1.rgb=vec3(1.0,1.0,1.0);
    	}
     
    	texelIntermediaire=texel0*texel1;//mix(texel0, texel1, 0.001);
    	texel3=texture2D(texShadowMap,gl_TexCoord[3].st);
    	texel=mix(texelIntermediaire, texel3, 0.0);
    	//texel=texelIntermediaire;
     
    	ct = texel.rgb;
    	at = texel.a;
     
    	gl_FragColor = vec4(ct /** cf*/, at /** af*/);	
    }
    Et enfin un screenshot du résultat:



    Donc voila si vous avez des idées n'hésitez pas.

    Merci.

  4. #4
    Membre averti
    Profil pro
    Responsable technique
    Inscrit en
    Février 2006
    Messages
    363
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Responsable technique

    Informations forums :
    Inscription : Février 2006
    Messages : 363
    Points : 353
    Points
    353
    Par défaut
    Bonjour à tous,

    Ça y est, j'ai, enfin, réussi à implémenter un PSSM correcte. Actuellement, il me reste plus qu'une choses à régler pour que je sois satisfait de mon algo. je vous explique le problème. Dans mon moteur, pour les maps, j'utilise les bsp de quake 3 avec frustum culling et PVS (classique). J'ai commencé par faire comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Pour chaque split
        Calculer le near, le far et le frustum de la lumière
        Set de la projection matrix et de la modelview matrix avec ces valeurs
        Affichage de la scene
     
        Restaure la projection matrix et la modelview matrix pour l''affichage du    point de vue du joueur
        Affichage de la scene
     
    Fin de la boucle
    Lors de l'affichage de ma scène, le frustum utilisé pour faire le frustum culling est calculé à partir des glGetFloatv(GL_PROJECTION_MATRIX,...) et glGetFloatv(GL_MODELVIEW_MATRIX,...).

    Sachant que la position de ma lumière est assez élevé, je me retrouvais à afficher la map entière lors du rendu de la shadow map. Le frustum culling ne servait donc à rien et les perfs étaient horrible.

    Je me suis donc dit que ca ne servait à rien de rendre autant de choses dans la shadow map et que les seuls éléments qui doivent être rendu dans la shadow map sont les éléments vu par la caméra du joueur.

    J'ai donc modifié ma fonction d'affichage de la map de telle sorte que les appels à glGetFloatv(GL_PROJECTION_MATRIX,...) et glGetFloatv(GL_MODELVIEW_MATRIX,...) ne soient plus présent. Je gère donc moi meme le calcul du frustum utilisé pour chaque rendu de la map et les performances sont nickel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Pour chaque split
        Calculer le frustum de la camera du joueur
        Calculer le near, le far et le frustum de la lumière
        Set de la projection matrix et de la modelview matrix avec ces valeurs
        Affichage de la scene avec les valeurs de frustum de la camera du joueur
     
        Restaure la projection matrix et la modelview matrix pour l''affichage du    point de vue du joueur
        Affichage de la scene avec les valeurs de frustum de la camera du joueur
     
    Fin de la boucle
    Mais, le frustum fait tellement bien son travail que je me retrouve avec des effets de clipping des ombres du au fait que certains éléments ne sont pas visibles du point de vue de la caméra du joueur mais que les ombres de ces éléments le sont.

    Je souhaiterais donc savoir si vous avez des idées pour corriger ce problème ou une meilleure technique d'optimisation qui permettrait d'effectuer le rendu complet de la map dans la shadow tout en conservant de bonnes performances.

    Le problème se pose aussi lorsque le joueur a des arbres dans le dos. Ceux-ci ne sont pas visible mais leurs ombres sont censé l'être. Or elles ne s'affichent pas à cause du frustum qui vire les arbres du rendu.

    PS: Ci-joint des screenshots montrant l'effet de clipping des ombres. La position de la caméra ne change pas. Seule l'orientation de la caméra est légèrement modifiée.

    L'ombre correctement affichée:



    Avec une rotation de la caméra vers la droite:



    En tournant encore la caméra vers la droite:


Discussions similaires

  1. Shadow Map - Problème d'Ombres Projetées
    Par 686insomnia686 dans le forum DirectX
    Réponses: 11
    Dernier message: 22/08/2012, 14h57
  2. Réponses: 2
    Dernier message: 14/05/2012, 11h53
  3. Parallel-Split Shadow Maps
    Par zenux dans le forum OpenGL
    Réponses: 7
    Dernier message: 02/04/2012, 22h36
  4. Problème de shadow mapping
    Par bobbysinger dans le forum OpenGL
    Réponses: 4
    Dernier message: 07/04/2010, 10h28
  5. Parallel-Split Shadow Map comment le metre en application
    Par Trysac dans le forum Développement 2D, 3D et Jeux
    Réponses: 0
    Dernier message: 10/07/2009, 15h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo