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 specular = false;
float shininess = 0;
float specularCoefficient = 0;
float specularValue = 0;
// fog
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[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->intensityClamp = 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 = (m_step == 0x10); // just step 1.0 ?
m_vpAmbient = vp->lightingParams[4]; // cache this
@ -1002,13 +1002,9 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
currentMesh->layered = true;
}
if (currentMesh->lighting) {
if (ph.SpecularEnabled()) {
currentMesh->specular = true;
currentMesh->shininess = 0;// ph.Shininess();
currentMesh->specularCoefficient = 0; // ph.SpecularValue();
}
}
currentMesh->specular = ph.SpecularEnabled();
currentMesh->shininess = ph.Shininess();
currentMesh->specularValue = ph.SpecularValue();
currentMesh->fogIntensity = ph.LightModifier();

View file

@ -315,7 +315,7 @@ bool PolyHeader::Layered()
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()
@ -399,6 +399,8 @@ UINT64 PolyHeader::Hash()
hash |= (UINT64)TextureAlpha() << 36; // bits 36 texture alpha processing
hash |= (UINT64)MicroTexture() << 37; // bits 37 microtexture 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

View file

@ -11,7 +11,7 @@ uniform float fogDensity;
uniform float fogStart;
uniform float modelScale;
//outputs to fragment shader
// outputs to fragment shader
varying float fsFogFactor;
varying vec3 fsViewVertex;
varying vec3 fsViewNormal; // per vertex normal vector
@ -51,10 +51,11 @@ uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates)
uniform vec3 spotColor; // spotlight RGB color
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 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 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 fogAttenuation;
uniform float fogAmbient;
@ -119,7 +120,7 @@ void main()
ellipse = 1.0 - ellipse; // invert
ellipse = max(0.0, ellipse); // clamp
if (lightEnable) {
if (lightEnabled) {
vec3 lightIntensity;
vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
@ -130,6 +131,9 @@ void main()
// Compute diffuse factor for sunlight
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
if(sunClamp) {
sunFactor = max(sunFactor,0.0);
@ -138,10 +142,7 @@ void main()
// Total light intensity: sum of all components
lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient
// Need a minimum clamp
lightIntensity = max(lightIntensity,0.0);
// Upper clamp is optional, some games will drive brightness beyond 100%
// Upper clamp is optional, step 1.5+ games will drive brightness beyond 100%
if(intensityClamp) {
lightIntensity = min(lightIntensity,1.0);
}
@ -166,12 +167,38 @@ void main()
finalData.rgb *= lightIntensity;
if (sunFactor > 0.0 && specularCoefficient > 0.0) {
float nDotL = max(dot(fsViewNormal,sunVector),0.0);
finalData.rgb += vec3(specularCoefficient * pow(nDotL,shininess));
if (specularEnabled) {
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
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_doubleSided = false;
m_lightEnabled = false;
m_specularEnabled = false;
m_layered = false;
m_textureInverted = false;
m_modelScale = 1.0f;
m_shininess = 0;
m_specularValue = 0;
m_microTexScale = 0;
m_baseTexSize[0] = 0;
m_baseTexSize[1] = 0;
m_shininess = 0;
m_specularCoefficient = 0;
m_microTexScale = 0;
m_matDet = MatDet::notset;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
@ -256,16 +283,19 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
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_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp");
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_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange");
m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale");
return success;
}
@ -334,18 +364,23 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
}
if (m_dirtyMesh || m->lighting != m_lightEnabled) {
glUniform1i(m_locLightEnable, m->lighting);
glUniform1i(m_locLightEnabled, m->lighting);
m_lightEnabled = m->lighting;
}
if (m_dirtyMesh || m->shininess != m_shininess) {
glUniform1f(m_locShininess, (m->shininess + 1) * 4);
glUniform1f(m_locShininess, m->shininess);
m_shininess = m->shininess;
}
if (m_dirtyMesh || m->specularCoefficient != m_specularCoefficient) {
glUniform1f(m_locSpecCoefficient, m->specularCoefficient);
m_specularCoefficient = m->specularCoefficient;
if (m_dirtyMesh || m->specular != m_specularEnabled) {
glUniform1i(m_locSpecularEnabled, m->specular);
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) {

View file

@ -32,27 +32,29 @@ private:
GLint m_locTexture2Enabled;
GLint m_locTextureAlpha;
GLint m_locAlphaTest;
GLint m_locMicroTexScale;
GLint m_locBaseTexSize;
GLint m_locTextureInverted;
// cached mesh values
bool m_textured1;
GLint m_locMicroTexScale;
GLint m_locBaseTexSize;
GLint m_locTextureInverted;
// cached mesh values
bool m_textured1;
bool m_textured2;
bool m_textureAlpha; // use alpha in texture
bool m_alphaTest; // discard fragment based on alpha (ogl does this with fixed function)
float m_fogIntensity;
bool m_doubleSided;
bool m_lightEnabled;
float m_shininess;
float m_specularCoefficient;
bool m_layered;
float m_microTexScale;
float m_baseTexSize[2];
bool m_textureInverted;
// cached model values
enum class MatDet { notset, negative, positive, zero };
bool m_doubleSided;
bool m_lightEnabled;
float m_shininess;
float m_specularValue;
bool m_specularEnabled;
bool m_layered;
float m_microTexScale;
float m_baseTexSize[2];
bool m_textureInverted;
// cached model values
enum class MatDet { notset, negative, positive, zero };
MatDet m_matDet;
float m_modelScale;
@ -70,18 +72,20 @@ private:
// lighting / other
GLint m_locLighting;
GLint m_locLightEnable;
GLint m_locSunClamp;
GLint m_locIntensityClamp;
GLint m_locShininess;
GLint m_locSpecCoefficient;
GLint m_locSpotEllipse;
GLint m_locSpotRange;
GLint m_locSpotColor;
GLint m_locSpotFogColor;
// model uniforms
GLint m_locModelScale;
GLint m_locLightEnabled;
GLint m_locSunClamp;
GLint m_locIntensityClamp;
GLint m_locShininess;
GLint m_locSpecularValue;
GLint m_locSpecularEnabled;
GLint m_locSpotEllipse;
GLint m_locSpotRange;
GLint m_locSpotColor;
GLint m_locSpotFogColor;
// model uniforms
GLint m_locModelScale;
};
} // New3D

View file

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