few problems wiht my render engine

Hello to all,

I have been working on a small render engine to learn opengl and improve my C++ skill.

I have lots of difficulties but I managed to sort them out and learn a lot from them :rolleyes: . However I have few problems that at least for now I can not see a solution and I have some issues with the way I have implemented some things (bad design decision since it is my first render engine).

I will try to make this thread a short as possible and list my errors that I have (note i have implemented a simple include for shaders so that I will reuse something and make shaders look more elegant) :

[ol]
[li]In the camera class I have some weird behavior when I rotate it. https://github.com/lumx/GameEngine/blob/master/GameEngine/src/Core/Render/Camera.cpp
[/li][li]Shadow map are really wired for now. ( look at the picture below) https://github.com/lumx/GameEngine/blob/master/GameEngine/src/Core/Render/Renderer.cpp, vertex shader https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/Shadow_vs.glsl fragment shader https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/Shadow_fs.glsl
[/li][li]I have specular color at the dark side of objects. ( look at the picture below) vertex shader https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/DirectionalLight_vs.glsl ,fragment shader https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/DirectionalLight_fs.glsl, global light include file https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/GlobalLight.inc
[/li][li]I have the OSD class but if I un-comment the error checking I get an INVALID_OPERATION in textures when drawing them. The result is correct but it brothers me why I have that error and why I don’t understand and fix it.
[/li][li]There is another issue with SDL where the mouse event just floods everything. I can not use the keyboard until all mouse motion events are processed and sometimes I can’t even close the window until they are finished. If you have any idea for this non opengl issue please let me know. It will help me a lot :smiley:
[/li][/ol]

As for the engine I would like to have few opinions if possible on how can I improve it especially how to handle shaders (attributes and uniforms better) and how to improve my texture class and use it as render target for FBO and RBO.

The code is on github https://github.com/lumx/GameEngine and the project is on this dropbox https://www.dropbox.com/s/hdxf0fefftgd6nz/GameEngine%204-April-15.7z?dl=0 link just in case you need a library or something else.

These are images of the shadow map and the depth-buffer

[ATTACH=CONFIG]975[/ATTACH]

[ATTACH=CONFIG]974[/ATTACH]

OK i managed to fix the shadows, however i have a question.

I had to increase the light projection matrix in order to fit the camera projection. Is there a way depending on the camera projection and position to set up the light projection to cover all the area that camera can project on the screen.

The problem is, if the light is too close to the mesh it won’t cover the whole mesh and I will end up having only parts of the mesh under shadow and weird shadows. I did increase the light projection but I get very low resolution shadows.

I am trying to Google the solution for this but I might not know the terms and I end up looking at the wrong places.

The image shows the current low res shadow with big projection glm::ortho(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 200.0f);
[ATTACH=CONFIG]978[/ATTACH]

and this one is with high res shadow and low projection glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, -10.0f, 20.0f);
[ATTACH=CONFIG]979[/ATTACH]

NOTE:: the shadow resolution is kept the same in both cases at 800x600 the actual screen size

Thank in advance

It can be done, but it’s not a simple problem. It requires a trade-off between minimising the size of the projected region and minimising the amount of CPU time required to compute it. An optimal fit can be very expensive in terms of CPU usage.

Also, dynamically adjusting the lighting projection to bound the visible portion of the world results in the projected shadow changing dynamically, which can look rather ugly. Particularly for a first-person view where the draw distance can vary wildly from one frame to the next.

[QUOTE=GClements;1265447]It can be done, but it’s not a simple problem. It requires a trade-off between minimising the size of the projected region and minimising the amount of CPU time required to compute it. An optimal fit can be very expensive in terms of CPU usage.

Also, dynamically adjusting the lighting projection to bound the visible portion of the world results in the projected shadow changing dynamically, which can look rather ugly. Particularly for a first-person view where the draw distance can vary wildly from one frame to the next.[/QUOTE]

Can you please help me find something that i can read about this problem just for curiosity. Since i am just learning and i am not really concerned about performance I would like to try and implement it and see how it works. Also what is the solutions that they use to avoid this in games since there are lots of shadow types and algorithms used for different purposes and different cases. As an example question, what do they do for sun light since it lights the whole map. Not going to ask about Crysis or Far Cry, shadows on tree leaves and shadows interacting with each other, etc ( I have seen PP presentation from previous GDC), just simple explanation or a lead to article where i can read something :D.

I am sorry i have so many questions, but i am learning a lot and it is getting fun the more i know.

Also is there any explanation why i have specular light at the opposite site. This is the hole include file where i will keep all my methods to calculate light https://github.com/lumx/GameEngine/blob/master/GameEngine/Assets/Shaders/GlobalLight.inc

This is the code sniper used for directional light.



struct Light
{
	vec3 color;
	float intensity;
	float ambient;
};

struct DirectionalLight
{
	Light light;
	vec3 direction;
};

vec4 GAMMA = vec4(1.0/2.2); // Implement gamma correction after fixing all the errors with directional light

vec4 calculateLightWithMaterial(Light light, vec3 lightDirection, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord){

	vec3 diffuseFactor = vec3(1.0,1.0,1.0);
	vec3 specularColor = vec3(1.0,1.0,1.0);
	
	diffuseFactor = (material.diffuse * light.color) * (light.intensity * clamp(dot(normal, lightDirection), 0.0, 1.0)) * vec3(texture(material.texture.diffuse,  texCoord.st)); 
	
	vec3 viewDir = normalize(eyePosition - worldPosition);
	vec3 reflectDir = normalize(reflect(-lightDirection, normal)); 

	//float specularFactor = pow(dot(viewDir, reflectDir),  material.shininess);
	float specularFactor = clamp(pow(dot(viewDir, reflectDir), material.shininess), 0.0, 1.0);
	specularColor = (light.color * material.specular) * (specularIntensity * specularFactor) * vec3(texture(material.texture.specular, texCoord.st));

	return vec4(diffuseFactor + specularColor + light.ambient, 1.0);
}


this is the material include file


struct Texture {
	sampler2D diffuse;
	sampler2D specular;
	sampler2D normal;
	sampler2D ambient;
	sampler2D height; // for bump/parallax maps
	//vec2 texCoord;			
};

struct Material {
	vec3 ambient;			// Ka
	vec3 diffuse;			// Kd
	vec3 specular;			// Ks
	vec3 transmittance;		// Tr
	vec3 emission;			// Ke
	float shininess;		// Ns
	float ior;				// Ni
	float dissolve;			// Dissolve
	int illum;				// Illum
	Texture texture;
};

uniform Material material;

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;

[QUOTE=lummxx;1265462]Can you please help me find something that i can read about this problem just for curiosity. Since i am just learning and i am not really concerned about performance I would like to try and implement it and see how it works.
[/QUOTE]
Determining an optimal projection is simple enough to describe: clip your world model to the view frustum, find a (roll) rotation which minimises the solid area of the frustum of the lighting projection, set the pitch and yaw rotations so that the projection is symmetric, apply a scale so that the aspect ratio of the projection matches that of the texture.

But the first two steps are likely to take far too long to be useful in practice.

The typical solution is cascaded shadow mapping, where multiple shadow maps of differing resolutions are used. The coarsest map would cover a fixed area of the world, finer-resolution maps would be (roughly) centred on the viewpoint (although you’d move them in multiples of the pixel size so that the shadows don’t “crawl”).

You need the clamp() inside the pow() (i.e. pow(clamp(dot(…))))

pow() is undefined in cases where its first argument is negative. E.g. it may be calculated as pow(x,y)=exp(log(x)*y), and log(x) is undefined for negative x.

Even if pow() produces the correct result, if material.shininess is an even integer (or a rational with an even numerator), x^(2*k) = (x^2)^k and (-x)^2 = x^2, so the function is symmetric about zero.

Moving the clamp() inside the pow() is sufficient; you don’t also need to clamp the result. If 0≤x≤1 and y>0, then 0≤xy≤1.

Yes, I should clamp the dot product before doing the power.:doh:

I did not see that for many days until now. Directional light is fixed and gamma correction is added.



vec4 calculateLightWithMaterial(Light light, vec3 lightDirection, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
	vec3 diffuseFactor = vec3(1.0f, 1.0f, 1.0f);
	vec3 specularColor = vec3(1.0f, 1.0f, 1.0f);
	
	diffuseFactor = (material.diffuse * light.color * vec3(texture(material.texture.diffuse, texCoord.st)) ) * (light.intensity * clamp(dot(normal, lightDirection), 0.0f, 1.0f) ) ; 
	
	vec3 viewDir = normalize(eyePosition - worldPosition);
	vec3 reflectDir = normalize(reflect(-lightDirection, normal)); 

	float specularFactor = pow(clamp(dot(viewDir, reflectDir), 0.0f, 1.0f), specularIntensity) ;
	
	specularColor = (light.color * material.specular * vec3(texture(material.texture.specular, texCoord.st)) ) * (specularFactor * material.shininess) ;

	return vec4(pow((diffuseFactor + specularColor + light.ambient + material.ambient), GAMMA), 1.0f);
}

Ok i guess i have to ask another question. I have the camera class that i use quaternion to rotate it, but the camera is not clamping like it should for a FPS-like camera and they behave weird. Hopefully I have something small that I can’t see like the clamp problem with specular factor. Also the event poling is a mess, when i use keyboard mouse motion does not work well and vice-verse.


void Camera::update(Input* input, float delta) {

	glm::vec3 direction = glm::normalize(cameraLook_ - cameraPosition_) * (cameraSpeed_ * delta);
	glm::vec3 strafeDirection = glm::normalize(glm::cross(direction, WORLD_YAXIS)) * (cameraSpeed_ * delta);

	switch (input->getSdlEvent()->type)
	{
	case SDL_MOUSEBUTTONDOWN:

		/*if (input->isMouseDown(SDL_BUTTON_LEFT)){
			//log_.debug(std::string("SDL_MOUSEBUTTONDOWN SDL_BUTTON_LEFT"));
		}

		if (input->isMouseDown(SDL_BUTTON_RIGHT)){
			//log_.debug(std::string("SDL_MOUSEBUTTONDOWN SDL_BUTTON_RIGHT"));
		}

		if (input->isMouseDown(SDL_BUTTON_MIDDLE)){
			log_.debug(std::string("SDL_MOUSEBUTTONDOWN SDL_BUTTON_MIDDLE"));
		}*/

		// FIXME :: Fix when the mouse has huge offset
		if (oldX !=0 || oldY != 0){
			//if (oldX < -1  || oldY < -1 || oldX > 1 || oldY > 1){
			//log_.debug(std::string("SDL_MOUSEBUTTONDOWN if (oldX > 1 && oldY > 1)"));
			oldX = input->getMousePosition().x;
			oldY = input->getMousePosition().y;
			return;
		}

		break;
	//case SDL_MOUSEBUTTONUP:
		//log_.debug(std::string("SDL_MOUSEBUTTONUP"));
		//break;
	case SDL_MOUSEMOTION:
		if (input->getIsMouseDown()){

			glm::vec2 mouseDelta = glm::vec2(oldX, oldY) - input->getMousePosition();
			if (mouseDelta.x == 0 && mouseDelta.y == 0)
				return;

			//pitch_ += glm::radians(mouseDelta.y);
			//yaw_ += glm::radians(mouseDelta.x);

			//pitch_ += mouseDelta.y * delta;
			//yaw_ += mouseDelta.x * delta;
			
			//clampAngles();
				
			/*log_.info(std::string("yaw_") +  std::to_string(yaw_));
			log_.info(std::string("pitch_") +  std::to_string(pitch_));

			log_.info(std::string("mouseDelta.x  ") + std::to_string(mouseDelta.x));
			log_.info(std::string("mouseDelta.Y  ") + std::to_string(mouseDelta.y));

			log_.info(std::string("glm::radians(-mouseDelta.x)  ") + std::to_string(glm::radians(mouseDelta.x)));
			log_.info(std::string("glm::radians(-mouseDelta.y)  ") + std::to_string(glm::radians(mouseDelta.y)));*/
			
			// FIXME :: clamp for rotation 
			/*glm::quat q1;
			if (pitch_ <= -M_PI_FLOAT_HALF || pitch_ >= M_PI_FLOAT_HALF)
				q1 = glm::quat(glm::vec3(0.0f, glm::radians(mouseDelta.x), 0.0f));
			else if (yaw_ <= -M_PI_FLOATx2 || yaw_ >= M_PI_FLOATx2)
				 q1 = glm::quat(glm::vec3(glm::radians(mouseDelta.y), 0.0f, 0.0f));
			else
				 q1 = glm::quat(glm::vec3(glm::radians(mouseDelta.y), glm::radians(mouseDelta.x), 0.0f));*/

			glm::quat q1 = glm::quat(glm::vec3(glm::radians(mouseDelta.y), glm::radians(mouseDelta.x), 0.0f));
			cameraLook_ = q1 * (direction * mouseSensitivity_) * glm::conjugate(q1) + cameraPosition_;

		}
		oldX = input->getMousePosition().x;
		oldY = input->getMousePosition().y;
		break;
	case SDL_KEYDOWN:

		if (input->isKeyDown(SDL_SCANCODE_A)) {
			//lookAt_ = glm::translate(lookAt_, -strafeDirection * (cameraSpeed_ * delta));
			cameraPosition_ -= strafeDirection ;
			cameraLook_ -= strafeDirection ;
		}

		if (input->isKeyDown(SDL_SCANCODE_D)){
			//lookAt_ = glm::translate(lookAt_, strafeDirection * (cameraSpeed_ * delta));
			cameraPosition_ += strafeDirection ;
			cameraLook_ += strafeDirection ;
		}

		if (input->isKeyDown(SDL_SCANCODE_W)){
			//lookAt_ = glm::translate(lookAt_, -direction * (cameraSpeed_ * delta));
			cameraPosition_ += direction;
			cameraLook_ += direction;
		}

		if (input->isKeyDown(SDL_SCANCODE_S)){
			//lookAt_ = glm::translate(lookAt_, direction * (cameraSpeed_ * delta));
			cameraPosition_ -= direction;
			cameraLook_ -= direction;
		}

		if (input->isKeyDown(SDL_SCANCODE_SPACE)) {
			cameraPosition_.y += cameraSpeed_ * delta;
			cameraLook_.y += cameraSpeed_ * delta;
		}

		if (input->isKeyDown(SDL_SCANCODE_LCTRL)) {
			cameraPosition_.y -= cameraSpeed_ * delta;
			cameraLook_.y -= cameraSpeed_ * delta;
		}
 
		break;
		//case SDL_KEYUP:
		//	if (Input::Instance(sdl_event)->isKeyUp(SDL_SCANCODE_LEFT))
		//		log_.info(std::string("SDL_KEYUP Left Button"));
		//	break;
	default:
		break;
	}

	if (isFreeLookCamera_) {
		//for free look camera 
		glm::vec3 upVector = glm::normalize(glm::cross(direction, strafeDirection));

		lookAt_ = glm::lookAt(
			cameraPosition_,			// the position of your camera, in world space
			cameraLook_,			// where you want to look at, in world space
			upVector			// probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too
			);
	} else {
		lookAt_ = glm::lookAt(
			cameraPosition_,			// the position of your camera, in world space
			cameraLook_,			// where you want to look at, in world space
			WORLD_YAXIS			// probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too
			);
	}
}

Specular texture is working fine always. I had to move around with camera to see that specular reflection. So textures bind is working fine so far.
[ATTACH=CONFIG]984[/ATTACH]

I will try to see what i can do with the projection and try to read more about shadows but they are very hard for me to understand now. I need 1 month to sit and read high school math and remember derivatives, integral, and trigonometry. Next step is to finish implementing other light types and move to bump maps, SSAO, then most probably i might try to implement a Differed shading . Then most probably code cleanup and change few bad designs that i have.

So far the journey has been fun but i regret i didn’t start asking here more things before and i tried to figure my way out myself. The whole thing goes much much faster when asking here and I learn more :D.

Thank you guys for support and help :biggrin-new:

I have few problems with spotlight, I just can’t figure out what is going one and i could not find any windows executable for a GLSL debugger today in the morning.

I need help with both especially for GLSL debugger because it will help me a lot to debug and learn more from debugging. I am using code XL though for now.

My shader code is like this ( have included also the comments that i did experiment last night). Sometimes i hard code the values in shader instead of changing them in the program because it is faster to re run the program compared to recompiling.


vec3 GAMMA = vec3(1.0/2.2);

vec4 calculateDirectionalLight(Light light, vec3 direction, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
	vec3 diffuseFactor = ( light.color * material.diffuse * vec3(texture(material.texture.diffuse, texCoord.st)) )
						 * (light.intensity * clamp(dot(normal, direction), 0.0f, 1.0f) ) ; 
	
	vec3 viewDir = normalize(eyePosition - worldPosition);
	vec3 reflectDir = normalize(reflect(-direction, normal)); 

	float specularFactor = pow(clamp(dot(viewDir, reflectDir), 0.0f, 1.0f), specularIntensity);
	
	vec3 specularColor = ( light.color * material.specular * vec3(texture(material.texture.specular, texCoord.st)) ) * (specularFactor * material.shininess);
	
	return vec4(pow((diffuseFactor + specularColor + light.ambient + material.ambient), GAMMA), 1.0f);
}

vec4 calculatePointLight(PointLight pointLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
	// DO NOT NORMALIZE lightDirection, WE NEED IT TO CALCULATE THE DISTANCE TO COMPARE RANGE OF LIGHT 
	vec3 lightDirection = pointLight.position - worldPosition; 
    float distanceToPoint = length(lightDirection);
	
	// I dont like conditionals in shader, but since this is fragment based lighting i believe 
	// this will speed-up things insetead of calculating the light
	if(distanceToPoint > pointLight.range)
		return vec4(0,0,0,0);

	vec4 light = calculateDirectionalLight(pointLight.light, lightDirection, normal, worldPosition,  specularIntensity, eyePosition, material, texCoord);
	
	// light attenuateion explained https://developer.valvesoftware.com/wiki/Constant-Linear-Quadratic_Falloff
	// http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Light+Attenuation+Shortcut
	float attenuation = max(pointLight.atten.constant
						+ pointLight.atten.linear * distanceToPoint
						+ pointLight.atten.quadratic * distanceToPoint * distanceToPoint,1); // there is no increasing attenuation :D
	
    return light / attenuation;
}

vec4 calculateSpotLight(SpotLight spotLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord) 
{
   
	vec4 light = vec4(0,0,0,0);
	vec3 lightDirection = vec3(0,15,0) - worldPosition;
	vec3 spotLightDirection = spotLight.pointLight.position - spotLight.lookAt;

	//float spotFactor = dot(lightDirection, spotLightDirection); // ??? something is wrong here
	float spotFactor = dot(lightDirection, spotLight.direction); // ??? something is wrong here

	//float spotFactor = dot(lightDirection, spotLightDirection);

	//if(spotFactor > spotLight.cutOff) // ?????
	if(spotFactor > 1.0)
	{
		light = calculatePointLight(spotLight.pointLight, normal, worldPosition, specularIntensity, eyePosition, material, texCoord); //* (1.0 - (1.0 - spotFactor)/(1.0 - spotLight.cutOff));
	}
    
    return light;
}

The black line that i have marked is where the light is at Y axis (the height). I dont know why is not a circle but it is a 180’ angle. After i finish this i will also add inner circle and outer circle from this tutorial http://www.ozone3d.net/tutorials/glsl_lighting_phong_p3.php

[ATTACH=CONFIG]996[/ATTACH]

the C++ code


        // how i create the spot light
	SpotLight* directionalLight = new SpotLight(glm::vec3(1, 1, 1), vec3(0, 0, 0),1.0f, 0.01f, glm::vec3(0.1, 25, 0.1), LightAttenuation(0.1, 0.01, 0.01), 45.0f);

      // spotlight class

SpotLight::SpotLight(glm::vec3 color, glm::vec3 position, float intensity, float ambient, glm::vec3 lightLookAt, LightAttenuation& lightAttenuation, float viewAngle) :
PointLight(color, position, intensity, ambient, lightAttenuation, ShaderProgram("Spot Light")),
//lightCutOff_(glm::cos(glm::radians(viewAngle / 2.0f))),
//lightCutOff_(glm::cos(viewAngle / 2.0f)),
//lightCutOff_(viewAngle / 2.0f),
lightLookAt_(lightLookAt)
{
	float tempAngle = glm::cos(viewAngle / 2.0f);
	const float pi = glm::pi<float>();

	if (tempAngle >= 0)
		lightCutOff_ = 0;
	else if (tempAngle <= pi)
		lightCutOff_ = pi;
	else
		lightCutOff_ = tempAngle;

	lightShader_.createShader(GL_VERTEX_SHADER, "Assets/Shaders/Light/Light_vs.glsl");
	lightShader_.createShader(GL_FRAGMENT_SHADER, "Assets/Shaders/Light/SpotLight_fs.glsl");
	lightShader_.createProgram();

	setUniforms();
}

if you want to see the full code please find it in the github rep https://github.com/lumx/GameEngine

Guys, thanks again for help :smiley:

Note : I need to finish the lights and shadows before i move to implementing config reader, create the world class that will hold all entities such as mesh, lights, camera,etc, recompile the shader if i modify it and i will change the code to use more C++11.