GLSL多光照模型

"游戏引擎"

Posted by A-SHIN on January 1, 2018

“Yeah It’s on. ”

前言

用GLSL实现ambient direction point spot multi-lighting

正文

首先vert shader 输出每个点世界空间中的坐标和法线(##如果是非统一缩放,计算法线时需要乘以逆转置矩阵##)

// lighting world space
out vec4 vs_fs_normal;
out vec4 vs_fs_position;
// lighting world space TODO: 逆转置矩阵
vs_fs_normal = model_matrix * vec4(normal,0);
vs_fs_position = model_matrix * position;

然后frag shader 定义灯光信息结构体数组从应用程序传入,根据光照类型分别进行散射、反射值计算

uniform vec3 ambient;
uniform vec3 eyePos;
in vec4 vs_fs_normal;
in vec4 vs_fs_position;
struct LightInfo
{
	bool isEnable;
	int lightType;
	vec3 lightColor;
	vec3 lightDir;
	float shininess;
	float strength;
	vec3 lightPos;
	float constantAttenuation;	// 衰减系数
	float LinearAttenuation;
	float QuadraticAttenuation;
	vec3 ConeDirection;
	float SpotCosCutoff;
	float SpotExponent;
};
const int MaxLights = 10;
uniform LightInfo Lights[MaxLights];

方向光计算:

void DirectionalLight(vec3 eyeDir, vec3 lightDir, vec3 normal, vec3 lightColor, float shininess, float strength, inout vec3 diff, inout vec3 spec)
{
	vec3 halfDir = normalize(eyeDir + lightDir);
	float diffuse = max(0.0, dot(normal, lightDir));
	float specular = max(0.0, dot(normal, halfDir));
	if(diffuse == 0.0)
	{
		specular = 0.0;
	}
	else
	{
		specular = pow(specular, shininess) * strength;
	}
	
	diff += lightColor * diffuse;
	spec += lightColor * specular;
}

点光源计算:

void PointLight(vec3 eyeDir, vec3 lightDir, vec3 normal, vec3 lightColor, float shininess, float strength,
float constantAttenuation, float LinearAttenuation, float QuadraticAttenuation, inout vec3 diff, inout vec3 spec)
{
	float lightDistance = length(lightDir);
	lightDir = lightDir/lightDistance;
	float attenuation = 1.0 / (constantAttenuation + LinearAttenuation*lightDistance + QuadraticAttenuation*lightDistance*lightDistance);
	vec3 halfDir = normalize(lightDir + eyeDir);
	float diffuse = max(0.0, dot(normal, lightDir));
	float specular = max(0.0, dot(normal, halfDir));
	if(diffuse == 0.0)
	{
		specular = 0.0;
	}
	else
	{
		specular = pow(specular, shininess) * strength;
	}
	
	diff += lightColor * diffuse * attenuation;
	spec += lightColor * specular * attenuation;
}

聚光灯计算:

void SpotLight(vec3 eyeDir, vec3 lightDir, vec3 normal, vec3 lightColor, float shininess, float strength,
float constantAttenuation, float LinearAttenuation, float QuadraticAttenuation, 
vec3 ConeDirection, float SpotCosCutoff, float SpotExponent, inout vec3 diff, inout vec3 spec)
{
	float lightDistance = length(lightDir);
	lightDir = lightDir/lightDistance;
	float attenuation = 1.0 / (constantAttenuation + LinearAttenuation*lightDistance + QuadraticAttenuation*lightDistance*lightDistance);
	float spotCos = dot(lightDir, -ConeDirection);
	if(spotCos < SpotCosCutoff)
	{
		attenuation = 0.0;
	}
	else
	{
		attenuation *= pow(spotCos, SpotExponent);
	}
	vec3 halfDir = normalize(lightDir + eyeDir);
	float diffuse = max(0.0, dot(normal, lightDir));
	float specular = max(0.0, dot(normal, halfDir));
	if(diffuse == 0.0)
	{
		specular = 0.0;
	}
	else
	{
		specular = pow(specular, shininess) * strength;
	}
	
	diff += lightColor * diffuse * attenuation;
	spec += lightColor * specular * attenuation;
}

main函数中多个光照效果合并:

vec3 ambi = ambient;
vec3 diff = vec3(0);
vec3 spec = vec3(0);
vec3 eyeDir = eyePos - vs_fs_position.xyz;
for(int i = 0; i < MaxLights; ++i)
{
	if(!Lights[i].isEnable)
	{
		continue;
	}
	if(Lights[i].lightType == 1)
	{
		// 平行光
		DirectionalLight(normalize(eyeDir), normalize(Lights[i].lightDir), normalize(vs_fs_normal.xyz), Lights[i].lightColor, Lights[i].shininess, Lights[i].strength, diff, spec);
	}
	else if(Lights[i].lightType == 2)
	{
		// 点光源
		vec3 lightDir = Lights[i].lightPos - vs_fs_position.xyz;
		PointLight(normalize(eyeDir), lightDir, normalize(vs_fs_normal.xyz), Lights[i].lightColor, Lights[i].shininess, Lights[i].strength, Lights[i].constantAttenuation, Lights[i].LinearAttenuation, Lights[i].QuadraticAttenuation, diff, spec);
	}
	else if(Lights[i].lightType == 3)
	{
		// 聚光灯
		vec3 lightDir = Lights[i].lightPos - vs_fs_position.xyz;
		SpotLight(normalize(eyeDir), lightDir, normalize(vs_fs_normal.xyz), Lights[i].lightColor, Lights[i].shininess, Lights[i].strength, 
		Lights[i].constantAttenuation, Lights[i].LinearAttenuation, Lights[i].QuadraticAttenuation, 
		normalize(Lights[i].ConeDirection), Lights[i].SpotCosCutoff, Lights[i].SpotExponent, diff, spec);
	}
}
vec3 scatteredLight = ambi + diff;
vec3 reflactedLight = spec;
vec3 rgb = min(fColor.rgb * scatteredLight + reflactedLight, vec3(1.0));
fColor = vec4(rgb, fColor.a);

应用程序自定义结构体数组uniform传入:

// light
for (int index = 0; index < HXSceneManager::GetInstance()->lightVct.size(); ++index)
{
	HXLight* light = HXSceneManager::GetInstance()->lightVct[index];
	if (light)
	{
		std::stringstream ss;
		std::string strIndex;
		ss << index;
		ss >> strIndex;
		std::string strLight = "Lights[" + strIndex + "]";
		property_loc = glGetUniformLocation(program, (strLight + ".isEnable").c_str());
		GLint isEnable = light->enable;
		glUniform1i(property_loc, isEnable);

		property_loc = glGetUniformLocation(program, (strLight + ".lightType").c_str());
		GLint lightType = light->lightType;
		glUniform1i(property_loc, lightType);

		property_loc = glGetUniformLocation(program, (strLight + ".lightColor").c_str());
		HXCOLOR lightColor = light->color;
		glUniform3f(property_loc, lightColor.r / 255.0f, lightColor.g / 255.0f, lightColor.b / 255.0f);

		property_loc = glGetUniformLocation(program, (strLight + ".lightDir").c_str());
		HXVector3D lightDir = light->direct;
		glUniform3f(property_loc, lightDir.x, lightDir.y, lightDir.z);

		property_loc = glGetUniformLocation(program, (strLight + ".shininess").c_str());
		GLfloat shininess = light->shininess;
		glUniform1f(property_loc, shininess);

		property_loc = glGetUniformLocation(program, (strLight + ".strength").c_str());
		GLfloat strength = light->strength;
		glUniform1f(property_loc, strength);

		property_loc = glGetUniformLocation(program, (strLight + ".lightPos").c_str());
		HXVector3D lightPos = light->position;
		glUniform3f(property_loc, lightPos.x, lightPos.y, lightPos.z);

		property_loc = glGetUniformLocation(program, (strLight + ".constantAttenuation").c_str());
		GLfloat constantAttenuation = light->constantAttenuation;
		glUniform1f(property_loc, constantAttenuation);

		property_loc = glGetUniformLocation(program, (strLight + ".LinearAttenuation").c_str());
		GLfloat LinearAttenuation = light->LinearAttenuation;
		glUniform1f(property_loc, LinearAttenuation);

		property_loc = glGetUniformLocation(program, (strLight + ".QuadraticAttenuation").c_str());
		GLfloat QuadraticAttenuation = light->QuadraticAttenuation;
		glUniform1f(property_loc, QuadraticAttenuation);

		property_loc = glGetUniformLocation(program, (strLight + ".SpotCosCutoff").c_str());
		GLfloat SpotCosCutoff = light->SpotCosCutoff;
		glUniform1f(property_loc, SpotCosCutoff);

		property_loc = glGetUniformLocation(program, (strLight + ".SpotExponent").c_str());
		GLfloat SpotExponent = light->SpotExponent;
		glUniform1f(property_loc, SpotExponent);
		
		property_loc = glGetUniformLocation(program, (strLight + ".ConeDirection").c_str());
		HXVector3D ConeDirection = light->ConeDirection;
		glUniform3f(property_loc, ConeDirection.x, ConeDirection.y, ConeDirection.z);
	}
}

场景配置文件中的灯光数据:

<Lights>
	<Light Enable="1" LightType="1" Shininess="10" Strength="0.2" constantAttenuation="0" LinearAttenuation="0" QuadraticAttenuation="0" SpotCosCutoff="0" SpotExponent="0">
		<Color Cr="255" Cg="255" Cb="255"/>
		<Postion Px="0" Py="0" Pz="0"/>
		<Direct Dx="1" Dy="1" Dz="1"/>
		<ConeDirection Cx="0" Cy="0" Cz="0"/>
	</Light>
	<Light Enable="1" LightType="2" Shininess="10" Strength="0.1" constantAttenuation="0.05" LinearAttenuation="0.05" QuadraticAttenuation="0.01" SpotCosCutoff="0" SpotExponent="0">
		<Color Cr="255" Cg="0" Cb="0"/>
		<Postion Px="0" Py="1" Pz="0"/>
		<Direct Dx="0" Dy="0" Dz="0"/>
		<ConeDirection Cx="0" Cy="0" Cz="0"/>
	</Light>
	<Light Enable="1" LightType="2" Shininess="10" Strength="0.1" constantAttenuation="0.05" LinearAttenuation="0.05" QuadraticAttenuation="0.01" SpotCosCutoff="0" SpotExponent="0">
		<Color Cr="0" Cg="0" Cb="255"/>
		<Postion Px="10" Py="1" Pz="0"/>
		<Direct Dx="0" Dy="0" Dz="0"/>
		<ConeDirection Cx="0" Cy="0" Cz="0"/>
	</Light>
	<Light Enable="1" LightType="2" Shininess="10" Strength="0.1" constantAttenuation="0.05" LinearAttenuation="0.05" QuadraticAttenuation="0.01" SpotCosCutoff="0" SpotExponent="0">
		<Color Cr="0" Cg="255" Cb="0"/>
		<Postion Px="-10" Py="1" Pz="0"/>
		<Direct Dx="0" Dy="0" Dz="0"/>
		<ConeDirection Cx="0" Cy="0" Cz="0"/>
	</Light>
	<Light Enable="1" LightType="3" Shininess="10" Strength="0.1" constantAttenuation="0.05" LinearAttenuation="0.05" QuadraticAttenuation="0.01" SpotCosCutoff="0" SpotExponent="10">
		<Color Cr="255" Cg="0" Cb="255"/>
		<Postion Px="8" Py="2" Pz="-8"/>
		<Direct Dx="0" Dy="0" Dz="0"/>
		<ConeDirection Cx="0" Cy="-1" Cz="0"/>
	</Light>
</Lights>

效果图如下:

具体代码在此

后记

这里自定义结构体数组由每个着色器管理,后续考虑采用uniform block在着色器之间共享。