How does BasicEffect work in the background?

We have a project, that will release for most platforms using MonoGame, the MonoGame port is ready, just not that updated, our target is mainly PC.

We’re trying to compile a shader based on BasicEffect.fx, we will slowly transition from HLSL to GLSL. Have you faced a similar situation? If so, what would be an equivalent of BasicEffect.fx.

I’m most concerned on where the 3 lights come from, as we are able to calculate specular but having lights wrongly calculated.

How are you calculating the lighting term?

Why do you need an equivalent of BasicEffect instead of just using BasicEffect? If you want to see the shader code, it’s open source:

Lights come from the SharpGLTF runtime, as is. This is the last shader we tried, it’s super messy, I’ll try to update with a better write up. The faulty code is the one calling the ComputeLights function, it’s the same as the XNA fx, unless something else is going on:

#version 330

// This source is subject to a License.
// Please see the license.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.

@uniform:
	#define SKINNED_EFFECT_MAX_BONES   128

	uniform sampler2D Texture;
	//uniform vec4 InputClipRectangle;

	uniform vec4 DiffuseColor;
	uniform vec3 EmissiveColor;
	uniform vec3 SpecularColor;
	uniform float  SpecularPower;

	uniform vec3 DirLight0Direction;
	uniform vec3 DirLight0DiffuseColor;
	uniform vec3 DirLight0SpecularColor;

	uniform vec3 DirLight1Direction;
	uniform vec3 DirLight1DiffuseColor;
	uniform vec3 DirLight1SpecularColor;

	uniform vec3 DirLight2Direction;
	uniform vec3 DirLight2DiffuseColor;
	uniform vec3 DirLight2SpecularColor;

	uniform vec3 EyePosition;

	uniform vec3 FogColor;
	uniform vec4 FogVector;

	uniform mat4 World;
	uniform mat4 View;
	uniform mat4 Projection;
	uniform mat3 WorldInverseTranspose;

	uniform mat4 WorldViewProj;

	uniform float HasTexture;

	uniform mat4 BoneTransforms[SKINNED_EFFECT_MAX_BONES];

	uniform float IsSkinned;


@vertex_input:
	layout(location = 0) in vec3 VertexPosition;
	layout(location = 1) in vec3 VertexNormal;
	layout(location = 2) in vec2 VertexTexCoord;
	layout(location = 3) in vec4 VertexBlendIndex;
	layout(location = 4) in vec4 VertexWeight;

@vertex_output:
	out vec4 FragmentPositionPS;
	out vec2 FragmentTexCoord;
	out vec4 FragmentPositionWS;
	out vec3 FragmentNormalWS;
	out vec4 FragmentDiffuse;

@fragment_input:
	in vec4 FragmentPositionPS;
	in vec2 FragmentTexCoord;
	in vec4 FragmentPositionWS;
	in vec3 FragmentNormalWS;
	in vec4 FragmentDiffuse;

@fragment_output:
	layout(location = 0) out vec4 OutputColour;

@vertex:

	mat4 transpose(in mat4 inMatrix) {
		highp vec4 i0 = inMatrix[0];
		highp vec4 i1 = inMatrix[1];
		highp vec4 i2 = inMatrix[2];
		highp vec4 i3 = inMatrix[3];

		highp mat4 outMatrix = mat4(
			vec4(i0.x, i1.x, i2.x, i3.x),
			vec4(i0.y, i1.y, i2.y, i3.y),
			vec4(i0.z, i1.z, i2.z, i3.z),
			vec4(i0.w, i1.w, i2.w, i3.w)
		);

		return outMatrix;
	}

	float ComputeFogFactor(vec4 position)
	{
		return clamp(dot(position, FogVector), 0.0, 1.0);
	}
 

	void main()
	{
		if (IsSkinned == 1.0) 
		{

			vec4 skinningPosition = vec4(0.0, 0.0, 0.0, 0.0);
			float boneCount = 4;

			for (int i = 0; i < boneCount; i++)
			{
				skinningPosition += (BoneTransforms[int(VertexBlendIndex[i])] * vec4(VertexPosition, 1.0)) * VertexWeight[i];
			}

			FragmentPositionPS = WorldViewProj * skinningPosition;
			//FragmentPositionPS = WorldViewProj * animationMatrix * vec4(VertexPosition, 1.0);

			//FragmentPositionPS = WorldViewProj * Position;
			FragmentPositionWS = (World)*vec4(VertexPosition, 1.0);
			FragmentNormalWS = normalize(VertexNormal);

			//float FogFactor = ComputeFogFactor(vec4(VertexPosition, 1.0));
			//FragmentPositionWS = vec4(VertexPosition.xyz, 1.0);

			FragmentDiffuse = vec4(1, 1, 1, 1);

			FragmentTexCoord = VertexTexCoord;

			gl_Position = FragmentPositionPS;
		}
		else 
		{
			FragmentPositionPS = WorldViewProj * vec4(VertexPosition, 1.0);

			//FragmentPositionPS = WorldViewProj * Position;
			FragmentPositionWS = (World)*vec4(VertexPosition, 1.0);
			FragmentNormalWS = normalize(VertexNormal);

			//float FogFactor = ComputeFogFactor(vec4(VertexPosition, 1.0));
			//FragmentPositionWS = vec4(VertexPosition.xyz, 1.0);

			FragmentDiffuse = vec4(1, 1, 1, 1);

			FragmentTexCoord = VertexTexCoord;

			gl_Position = FragmentPositionPS;
		}
		
	}

@fragment:
	vec3 ApplyFog(vec4 color, float fogFactor)
	{
		return mix(color.rgb, FogColor * color.a, fogFactor);
	}


	vec3 AddSpecular(inout vec4 color, vec3 specular)
	{
		return specular * color.a;
	}


	struct ColorPair
	{
		vec3 Diffuse;
		vec3 Specular;
	};

	
	mat3 transpose(mat3 m) {
		return mat3(m[0][0], m[1][0], m[2][0],
			m[0][1], m[1][1], m[2][1],
			m[0][2], m[1][2], m[2][2]);
	}
	

	ColorPair ComputeLights(vec3 eyeVector, vec3 worldNormal, int numLights)
	{
		mat3 lightDirections = mat3(0);
		mat3 lightDiffuse = mat3(0);
		mat3 lightSpecular = mat3(0);
		mat3 halfVectors = mat3(0);

		for (int i = 0; i < numLights; i++)
		{
			lightDirections[i] = mat3(DirLight0Direction, DirLight1Direction, DirLight2Direction)[i];
			lightDiffuse[i] = mat3(DirLight0DiffuseColor, DirLight1DiffuseColor, DirLight2DiffuseColor)[i];
			lightSpecular[i] = mat3(DirLight0SpecularColor, DirLight1SpecularColor, DirLight2SpecularColor)[i];

			halfVectors[i] = normalize(eyeVector - lightDirections[i]);
		}


		vec3 dotL = -((lightDirections) * worldNormal);
		vec3 dotH = halfVectors * worldNormal;

		vec3 zeroL = step(vec3(0, 0, 0), dotL);

		vec3 diffuse = zeroL * dotL;
		vec3 specular = pow(max(dotH, 0) * zeroL, vec3(SpecularPower));

		ColorPair result;

		result.Diffuse = (lightDiffuse * diffuse) * DiffuseColor.rgb + EmissiveColor;
		result.Specular = (lightSpecular * specular) * SpecularColor;

		return result;
	}

	void main()
	{
		vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
		
		if (HasTexture == 1.0) 
		{
			color = texture2D(Texture, FragmentTexCoord);// * FragmentDiffuse;
		}

		vec3 lightDir = normalize(DirLight0Direction - FragmentPositionWS.xyz);

		float diff = max(dot(FragmentNormalWS, lightDir), 0.0);
		vec3 diffuse = diff * DirLight0DiffuseColor;

		vec3 eyeVector = normalize(EyePosition - FragmentPositionWS.xyz);
		vec3 worldNormal = FragmentNormalWS;//normalize(FragmentNormalWS);

		ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 1);

		color.rgb *= lightResult.Diffuse;

		color.rgb += AddSpecular(color, lightResult.Specular);
		//color.rgb += ApplyFog(color, FragmentPositionWS.w);

		// Multiply texture colour with blending colour

		//OutputColour = color;

		//OutputColour = vec4(diffuse, 1.0) * texture2D(Texture, FragmentTexCoord);

		OutputColour = texture2D(Texture, FragmentTexCoord);

	}

Because we’re using OpenGL for our PC target and MonoGame for Linux & MacOS. We have ported quite a few of the MonoGame classes to be used by the OpenTK environment.

Can you give a screen shot of what you get, and one of what you expect to compare?

Might be a daft Q, but you are passing light as a normalized direction and not a position right?

Monogame is just using traditional diffuse and specular lighting calculations nothing fancy at all lambertian.

something like.

diffuse = NdotL;
specular = pow( NdotL * NdotH,5f );
// … balance and combine them.