Sometime ago I managed to work out that specular on the model3 is not real specular, and really is just an extension of diffuse lighting. But attempts were derailed by corner cases and the fact we were not handling the normals correctly. Anyway Harry managed to successfully come up with an algorithm, and coefficients that give an almost perfect match to specular on the model3, based soley on observations from video footage! He also worked out that the lighting on hardware 1.5 onwards appears to be unclamped (ie greater than 1). This quite radically changes the brightness of some of the games, but much better matches the original hardware.

This commit is contained in:
Ian Curtis 2017-08-09 16:56:56 +00:00
parent 1d338877fc
commit 5709ee2659
6 changed files with 102 additions and 62 deletions

View file

@ -88,7 +88,7 @@ struct Mesh
bool lighting = false; bool lighting = false;
bool specular = false; bool specular = false;
float shininess = 0; float shininess = 0;
float specularCoefficient = 0; float specularValue = 0;
// fog // fog
float fogIntensity = 1.0f; float fogIntensity = 1.0f;

View file

@ -777,8 +777,8 @@ void CNew3D::RenderViewport(UINT32 addr)
vp->lightingParams[4] = (float)((vpnode[0x24] >> 8) & 0xFF) * (1.0f / 255.0f); // ambient intensity vp->lightingParams[4] = (float)((vpnode[0x24] >> 8) & 0xFF) * (1.0f / 255.0f); // ambient intensity
vp->lightingParams[5] = 0.0; // reserved vp->lightingParams[5] = 0.0; // reserved
vp->sunClamp = true; //TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data vp->sunClamp = 1; // TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data
vp->intensityClamp = true; //TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data vp->intensityClamp = (m_step == 0x10); // just step 1.0 ?
m_vpAmbient = vp->lightingParams[4]; // cache this m_vpAmbient = vp->lightingParams[4]; // cache this
@ -1002,13 +1002,9 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
currentMesh->layered = true; currentMesh->layered = true;
} }
if (currentMesh->lighting) { currentMesh->specular = ph.SpecularEnabled();
if (ph.SpecularEnabled()) { currentMesh->shininess = ph.Shininess();
currentMesh->specular = true; currentMesh->specularValue = ph.SpecularValue();
currentMesh->shininess = 0;// ph.Shininess();
currentMesh->specularCoefficient = 0; // ph.SpecularValue();
}
}
currentMesh->fogIntensity = ph.LightModifier(); currentMesh->fogIntensity = ph.LightModifier();

View file

@ -315,7 +315,7 @@ bool PolyHeader::Layered()
float PolyHeader::Shininess() float PolyHeader::Shininess()
{ {
return ((header[6] >> 5) & 3) / 3.0f; return (float)((header[6] >> 5) & 3); // input sdk values are float 0-1 output are int 0-3
} }
int PolyHeader::TexFormat() int PolyHeader::TexFormat()
@ -399,6 +399,8 @@ UINT64 PolyHeader::Hash()
hash |= (UINT64)TextureAlpha() << 36; // bits 36 texture alpha processing hash |= (UINT64)TextureAlpha() << 36; // bits 36 texture alpha processing
hash |= (UINT64)MicroTexture() << 37; // bits 37 microtexture enable hash |= (UINT64)MicroTexture() << 37; // bits 37 microtexture enable
hash |= (UINT64)HighPriority() << 38; // bits 38 high priority enable hash |= (UINT64)HighPriority() << 38; // bits 38 high priority enable
hash |= (UINT64)SpecularEnabled() << 39; // bits 39 enable specular reflection
hash |= (UINT64)SmoothShading() << 40; // bits 40 smooth shading
//to do add the rest of the states //to do add the rest of the states

View file

@ -51,10 +51,11 @@ uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates)
uniform vec3 spotColor; // spotlight RGB color uniform vec3 spotColor; // spotlight RGB color
uniform vec3 spotFogColor; // spotlight RGB color on fog uniform vec3 spotFogColor; // spotlight RGB color on fog
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform bool lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
uniform bool sunClamp; // not used by daytona and la machine guns uniform bool sunClamp; // not used by daytona and la machine guns
uniform bool intensityClamp; // some games such as daytona and uniform bool intensityClamp; // some games such as daytona and
uniform float specularCoefficient;// specular coefficient uniform bool specularEnabled; // specular enabled
uniform float specularValue; // specular coefficient
uniform float shininess; // specular shininess uniform float shininess; // specular shininess
uniform float fogAttenuation; uniform float fogAttenuation;
uniform float fogAmbient; uniform float fogAmbient;
@ -119,7 +120,7 @@ void main()
ellipse = 1.0 - ellipse; // invert ellipse = 1.0 - ellipse; // invert
ellipse = max(0.0, ellipse); // clamp ellipse = max(0.0, ellipse); // clamp
if (lightEnable) { if (lightEnabled) {
vec3 lightIntensity; vec3 lightIntensity;
vec3 sunVector; // sun lighting vector (as reflecting away from vertex) vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
@ -130,6 +131,9 @@ void main()
// Compute diffuse factor for sunlight // Compute diffuse factor for sunlight
sunFactor = dot(sunVector, fsViewNormal); sunFactor = dot(sunVector, fsViewNormal);
// Clamp ceil, fix for upscaled models without "modelScale" defined
sunFactor = clamp(sunFactor,-1.0,1.0);
// Optional clamping, value is allowed to be negative // Optional clamping, value is allowed to be negative
if(sunClamp) { if(sunClamp) {
sunFactor = max(sunFactor,0.0); sunFactor = max(sunFactor,0.0);
@ -138,10 +142,7 @@ void main()
// Total light intensity: sum of all components // Total light intensity: sum of all components
lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient
// Need a minimum clamp // Upper clamp is optional, step 1.5+ games will drive brightness beyond 100%
lightIntensity = max(lightIntensity,0.0);
// Upper clamp is optional, some games will drive brightness beyond 100%
if(intensityClamp) { if(intensityClamp) {
lightIntensity = min(lightIntensity,1.0); lightIntensity = min(lightIntensity,1.0);
} }
@ -166,12 +167,38 @@ void main()
finalData.rgb *= lightIntensity; finalData.rgb *= lightIntensity;
if (sunFactor > 0.0 && specularCoefficient > 0.0) { if (specularEnabled) {
float nDotL = max(dot(fsViewNormal,sunVector),0.0);
finalData.rgb += vec3(specularCoefficient * pow(nDotL,shininess)); float exponent, NdotL, specularFactor;
vec4 biasIndex, expIndex, multIndex;
// Always clamp floor to zero, we don't want deep black areas
NdotL = max(0.0,sunFactor);
expIndex = vec4(8.0, 16.0, 32.0, 64.0);
multIndex = vec4(2.0, 2.0, 3.0, 4.0);
biasIndex = vec4(0.95, 0.95, 1.05, 1.0);
exponent = expIndex[int(shininess)] / biasIndex[int(shininess)];
specularFactor = pow(NdotL, exponent);
specularFactor *= multIndex[int(shininess)];
specularFactor *= biasIndex[int(shininess)];
specularFactor *= specularValue;
specularFactor *= lighting[1].x;
if (colData.a < 1.0) {
/// Specular hi-light affects translucent polygons alpha channel ///
finalData.a = max(finalData.a, specularFactor);
}
finalData.rgb += vec3(specularFactor);
} }
} }
// Final clamp: we need it for proper shading in dimmed light and dark ambients
finalData.rgb = min(finalData.rgb, vec3(1.0));
// Spotlight on fog // Spotlight on fog
vec3 lSpotFogColor = spotFogColor * ellipse * fogColour.rgb; vec3 lSpotFogColor = spotFogColor * ellipse * fogColour.rgb;
@ -199,17 +226,17 @@ void R3DShader::Start()
m_alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function) m_alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function)
m_doubleSided = false; m_doubleSided = false;
m_lightEnabled = false; m_lightEnabled = false;
m_specularEnabled = false;
m_layered = false; m_layered = false;
m_textureInverted = false; m_textureInverted = false;
m_modelScale = 1.0f; m_modelScale = 1.0f;
m_shininess = 0;
m_specularValue = 0;
m_microTexScale = 0;
m_baseTexSize[0] = 0; m_baseTexSize[0] = 0;
m_baseTexSize[1] = 0; m_baseTexSize[1] = 0;
m_shininess = 0;
m_specularCoefficient = 0;
m_microTexScale = 0;
m_matDet = MatDet::notset; m_matDet = MatDet::notset;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
@ -256,17 +283,20 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient"); m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting"); m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting");
m_locLightEnable = glGetUniformLocation(m_shaderProgram, "lightEnable"); m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled");
m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp"); m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp");
m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp"); m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp");
m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess"); m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess");
m_locSpecCoefficient= glGetUniformLocation(m_shaderProgram, "specularCoefficient"); m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue");
m_locSpecularEnabled= glGetUniformLocation(m_shaderProgram, "specularEnabled");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse"); m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange"); m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange");
m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor"); m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor"); m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale"); m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale");
return success; return success;
} }
@ -334,18 +364,23 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
} }
if (m_dirtyMesh || m->lighting != m_lightEnabled) { if (m_dirtyMesh || m->lighting != m_lightEnabled) {
glUniform1i(m_locLightEnable, m->lighting); glUniform1i(m_locLightEnabled, m->lighting);
m_lightEnabled = m->lighting; m_lightEnabled = m->lighting;
} }
if (m_dirtyMesh || m->shininess != m_shininess) { if (m_dirtyMesh || m->shininess != m_shininess) {
glUniform1f(m_locShininess, (m->shininess + 1) * 4); glUniform1f(m_locShininess, m->shininess);
m_shininess = m->shininess; m_shininess = m->shininess;
} }
if (m_dirtyMesh || m->specularCoefficient != m_specularCoefficient) { if (m_dirtyMesh || m->specular != m_specularEnabled) {
glUniform1f(m_locSpecCoefficient, m->specularCoefficient); glUniform1i(m_locSpecularEnabled, m->specular);
m_specularCoefficient = m->specularCoefficient; m_specularEnabled = m->specular;
}
if (m_dirtyMesh || m->specularValue != m_specularValue) {
glUniform1f(m_locSpecularValue, m->specularValue);
m_specularValue = m->specularValue;
} }
if (m_dirtyMesh || m->layered != m_layered) { if (m_dirtyMesh || m->layered != m_layered) {

View file

@ -45,7 +45,9 @@ private:
bool m_doubleSided; bool m_doubleSided;
bool m_lightEnabled; bool m_lightEnabled;
float m_shininess; float m_shininess;
float m_specularCoefficient; float m_specularValue;
bool m_specularEnabled;
bool m_layered; bool m_layered;
float m_microTexScale; float m_microTexScale;
float m_baseTexSize[2]; float m_baseTexSize[2];
@ -70,11 +72,13 @@ private:
// lighting / other // lighting / other
GLint m_locLighting; GLint m_locLighting;
GLint m_locLightEnable; GLint m_locLightEnabled;
GLint m_locSunClamp; GLint m_locSunClamp;
GLint m_locIntensityClamp; GLint m_locIntensityClamp;
GLint m_locShininess; GLint m_locShininess;
GLint m_locSpecCoefficient; GLint m_locSpecularValue;
GLint m_locSpecularEnabled;
GLint m_locSpotEllipse; GLint m_locSpotEllipse;
GLint m_locSpotRange; GLint m_locSpotRange;
GLint m_locSpotColor; GLint m_locSpotColor;

View file

@ -1441,6 +1441,9 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data)); GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data));
break; break;
case 0x70:
printf("rar\n");
// Various // Various
case 0xF0: case 0xF0:
case 0xFE: // mirror case 0xFE: // mirror