Gamestudio Links
Zorro Links
Newest Posts
Zorro S for Oanda US
by AndrewAMD. 06/06/20 16:39
The Black Book
by SteveG. 06/06/20 14:01
Indicator coding
by danatrader. 06/06/20 11:53
New AUM
by Aku_Aku. 06/06/20 10:10
Reading a TXT file on server
by Truth. 06/05/20 18:22
Oanda instruments
by Morris. 06/05/20 10:24
AUM Magazine
Latest Screens
The Space Between
Pogostuck: Rage With Your Friends
Worst Case Z
AckCon'18 - Lotter vs the World 2 - Preview Release
Who's Online Now
4 registered members (AndrewAMD, SteveG, kalmar, Grat), 472 guests, and 7 spiders.
Key: Admin, Global Mod, Mod
Newest Members
Frank_Shieh, PBSeven, Hardi01, Giuseppe, AdamWu
18461 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Page 1 of 2 1 2
[SUB] compute the conic shape for SPOTLIGHT dyn lights #479190
02/27/20 20:01
02/27/20 20:01
Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
txesmi Offline OP
Serious User
txesmi  Offline OP
Serious User

Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
Hi!
I compute spotlights with a formula I build based on the result of dot and cross products of vectors. I don't know if there is a faster way...

Goemetrically, the dot product of two vectors returns the scalar of the orthogonal projection of the second vector over the first. This is the fastest way to compute the distance from a plane to a point. A plane can be described with a point and a normal which is precisely what we get as a description of a spotlight: a position and a direction (normal).

The length of the resulting vector of the cross product of two vectors is the area of the parallelogram described by the two vectors. This is the fastest way to compute the distance from a line to a point.This is harder to explain, but it happens that when the first vector is a unit vector, the value of the area is the same as the minimum distance between the second vector and the line that contains the first vector.

We start with:
- world_pos -> the world position of the rendered pixel.
- light_pos -> the world position of the light.
- light_dir -> the direction of the light.

The first thing we need to do is to transform the world position to a position referenced to the light position, so it describes the world position in the same coordinate system as the light direction.

Code
float3 light_ray = world_pos - light_pos;


Since the light direction is a unit vector, the result of the dot product will return exactly the depth in the spotlight. In other words, the distance from the point to the plane described by the light data.

Code
float depth = dot(light_dir, light_ray);


This depth can be easily transformed to a 0<->1 factor

Code
float depth_factor = saturate(depth / light_range); // saturate clips the value between 0 and 1, any negative value will result in 0, and any value above 1 wiil result in 1.


Since the light direction is a unit vector, the length of the result of the cross product will be exactly the distance between the point and the light direction line.

Code
float radius = length(cross(light_dir, light_ray));


This radius can be easily transformed to a 0<->1 factor when dividing it by the maximum radius: the light range. The conic shape is given by the multiply of the maximum radius by the depth factor so we get a larger maximum radius while the depth gets larger. Notice that the result of the division is linearly proportional to the radius, so we need to inverse it so the maximum lighting is in the light direction line. You can add a custom multiplo to the operation in order to get certain control over the cone proportions.

Code
float spot_factor = 1.0 - saturate(radius / (light_range * depth_factor * custom_factor));


This spot factor has to be multiplied by the attenuation factor based on the length of light_ray, but that is another story.

Salud!

Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479191
02/27/20 20:50
02/27/20 20:50
Joined: May 2009
Posts: 5,265
Caucasus
3run Online
Senior Expert
3run  Online
Senior Expert

Joined: May 2009
Posts: 5,265
Caucasus
Hey!

If I understand correctly, light_range is stored in vecLightPos.w, so all above should sum up to something like this ?
Code
float custom_factor = 1;

for(int i = 0; i < 8; i++)
{
	if(vecLightDir[i].w > 0) // spotlight ?
	{
		float3 light_ray = Out.WorldPos - vecLightPos[i].xyz;
		float depth = dot(vecLightDir[i], light_ray);
		
		// saturate clips the value between 0 and 1
		// any negative value will result in 0, and any value above 1 wiil result in 1.
		float depth_factor = saturate(depth / vecLightPos[i].w);
		float radius = length(cross(vecLightDir[i], light_ray));
		float spot_factor = 1.0 - saturate(radius / (vecLightPos[i].w * depth_factor * custom_factor));
	}
}


Greets


Looking for free stuff?? Take a look here: http://badcom.at.ua
Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479192
02/27/20 21:34
02/27/20 21:34
Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
txesmi Offline OP
Serious User
txesmi  Offline OP
Serious User

Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
Yes, but I should heve used the radius variable in the last code line instead of the whole cross product. I fixed it.

My last playground looks like this:
Code
float light = 0;
for(; light < iLights; light += 1.0) {
	// light ray
	float3 ray = vtx.wpos - vecLightPos[light].xyz;
	
	// spotlight factor
	float depth = saturate(dot(vecLightDir[light].xyz, ray) / vecLightPos[light].w);
	float spot = 1.0 - saturate(length(cross(vecLightDir[light].xyz, ray)) / (vecLightPos[light].w * depth));
	
	// normalize the light ray
	float dist = length(ray);
	ray /= dist; 
	
	// attenuation
	float att = 1.0 - saturate(dist / vecLightPos[light].w);
	
	// final light factor
	att *= vecLightDir[light].w ? spot : 1.0;
	
	// diffuse term
	diffuse += vecLightColor[light].rgb * saturate(-dot(ray, vtx.normal.xyz)) * att;
	
	// specular term
	refl = reflect(ray, vtx.normal.xyz);
	specular += vecLightColor[light].rgb * pow(saturate(-dot(refl, vecViewDir.xyz)), fPower) * att;
}



Salud!

Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479193
02/27/20 21:45
02/27/20 21:45
Joined: May 2009
Posts: 5,265
Caucasus
3run Online
Senior Expert
3run  Online
Senior Expert

Joined: May 2009
Posts: 5,265
Caucasus
Yes, I see. I got this code from your latest standardshader on GitHub, but it doesn't work for me in VS. maybe there are some limitations that make this work only in PS?

Edit: I'll post a screenshot with more detailed explanation tomorrow, it's too late today. Thank you.

Last edited by 3run; 02/27/20 21:53.

Looking for free stuff?? Take a look here: http://badcom.at.ua
Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: 3run] #479194
02/27/20 21:59
02/27/20 21:59
Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
txesmi Offline OP
Serious User
txesmi  Offline OP
Serious User

Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
You will need to use a high tesselation so the cone occupy more than a vertex. It happens same with the engine default shading, isn't it?

There are no extra limitations, afaik

Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479195
02/28/20 08:04
02/28/20 08:04
Joined: May 2009
Posts: 5,265
Caucasus
3run Online
Senior Expert
3run  Online
Senior Expert

Joined: May 2009
Posts: 5,265
Caucasus
Well, I guess this time I got them working correctly grin
At least to my eye, they seem to work fine (the only thing I could mention is that on low poly stuff spotlight brightness seems to be too weak).

Here is how my mtl_shaded.fx looks like:
Code
#include <pos>
#include <normal>
#include <lights>

float4x4 matWorldViewProj;
float4 vecViewPos;

float4 vecLightDir[8];

float4 vecSunColor;
float4 vecFog;
float4 vecFogColor;
float4 vecSkill1;

Texture entSkin1;
Texture entSkin2;

sampler ColorSampler = sampler_state
{
	Texture = <entSkin1>;
	Mipfilter = None;
	Minfilter = None;
	Magfilter = None;
};

sampler ShadowSampler = sampler_state
{
	Texture = <entSkin2>;
	Mipfilter = Linear;
	Minfilter = Linear;
	Magfilter = Linear;
};

struct vertexOut
{
	float4 Pos : POSITION;
	float4 Color : COLOR0;
	float3 Normal : NORMAL;
	float4 Tex : TEXCOORD0;
	float4 WorldPos : TEXCOORD1;
};

vertexOut VS (
in float4 inPos : POSITION,
in float3 inNormal : NORMAL,
in float2 inTex1 : TEXCOORD0,
in float2 inTex2 : TEXCOORD1
)
{
	inPos.w = 1.0f;	
	vertexOut Out;
	
	// vertex snapping
	float4 snapToPixel = mul(inPos, matWorldViewProj);
	float4 vertex = snapToPixel;
	vertex.xyz = snapToPixel.xyz / snapToPixel.w;
	vertex.x = floor((vecSkill1.y + 40) * vertex.x) / (vecSkill1.y + 40); // default 160
	vertex.y = floor(vecSkill1.y * vertex.y) / vecSkill1.y; // default 120
	vertex.xyz *= snapToPixel.w;
	Out.Pos = vertex;
	
	// affine texture mapping
	Out.Pos *= inPos.w / length(mul(inPos, matWorldViewProj));
	
	Out.Normal = normalize (mul(inNormal, (float3x3)matWorld));
	Out.Tex.xy = inTex1.xy;
	Out.Tex.zw = inTex2.xy;
	Out.WorldPos = mul(inPos, matWorld);
	Out.Color = 0;
	
	float3 P = DoPos(inPos);
	float3 N = DoNormal(inNormal);
	
	// multiple dynamic lights
	float light = 0;
	float lastLight = iLights - vecSunColor.w;
	for(; light < lastLight; light += 1.0)
	{
		// light ray
		float3 ray = Out.WorldPos - vecLightPos[light].xyz;
		
		// spotlight factor
		float depth = saturate(dot(vecLightDir[light].xyz, ray) / vecLightPos[light].w);
		float spot = 1.0 - saturate(length(cross(vecLightDir[light].xyz, ray)) / (vecLightPos[light].w * depth));
		
		// normalize the light ray
		float dist = length(ray);
		ray /= dist; 
		
		// attenuation
		float att = 1.0 - saturate(dist / vecLightPos[light].w);
		
		// final light factor
		float strength = vecLightDir[light].w ? spot * att : att;
		
		// diffuse term
		Out.Color.rgb += vecLightColor[light].rgb * saturate(-dot(ray, inNormal.xyz)) * strength;
	}
	
	// cut out polygons
	float distance = length(mul(inPos, matWorldViewProj));
	
	if(vecSkill1.z == 0){ vecSkill1.z = 512; }
	
	if (distance > vecSkill1.z)
	{
		Out.Pos.w = 0;
	}
	
	return Out;
}

float4 PS(vertexOut In) : COLOR0
{
	float4 textureColor = tex2D(ColorSampler, In.Tex.xy);
	float4 color = tex2D(ShadowSampler, In.Tex.zw);
	float fDepth = distance(vecViewPos.xyz, In.WorldPos.xyz);
	
	color += In.Color; // add dynamic lights
	
	color.rgb = saturate(color.rgb);
	color.rgb = pow(color.rgb, vecSkill1.x);
	color.rgb *= textureColor.rgb;
	
	float Fog = saturate((fDepth - vecFog.x) * vecFog.z);
	color.rgb = lerp(color.rgb, vecFogColor, Fog);
	
	color.a = 1;
	
	return color;
}

technique mtl_shaded
{
	pass one
	{
		ZWriteEnable = True;
		AlphaBlendEnable = False;
		AlphaTestEnable = False;
		
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PS();
	}
}
Also, the worldPos I use is float4 and the one used in your example is float3.. I tried to do
Code
float3 ray = Out.WorldPos.xyz - vecLightPos[light].xyz;
instead of
Code
float3 ray = Out.WorldPos - vecLightPos[light].xyz;
but it didn't work, so I left it as it is.

Here you can watch video on youtube (clickable screenshot)
[Linked Image]

Best regards

Edit: level is dark, that was made intentionally (it can be adjusted to make it very bright if needed).
Also, I noticed that the way you calculate shadows, make them more smooth almost like per pixel lightning, while the original one looked like this
[Linked Image]
Maybe making them a bit smoother is better.. but just in case, is there an easy way to make them edgier?

Edit2: adding custom strength factor seems to help a lot too! laugh I'm pretty happy with results! Thank you one more time man!
[Linked Image]

Last edited by 3run; 02/28/20 08:31.

Looking for free stuff?? Take a look here: http://badcom.at.ua
Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479205
02/28/20 20:41
02/28/20 20:41
Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
txesmi Offline OP
Serious User
txesmi  Offline OP
Serious User

Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
You are welcome wink

Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479210
03/01/20 19:44
03/01/20 19:44
Joined: May 2009
Posts: 5,265
Caucasus
3run Online
Senior Expert
3run  Online
Senior Expert

Joined: May 2009
Posts: 5,265
Caucasus
Hey!

Sorry for off-topic, but I wanted to ask for a little advice. How would you pass light calculations from VS to PS ?

Currently, I do all light calculations in VS, then in PS I read pixel color from texture and "add" it to the color which already has calculated lights. This isn't probably the best way to handle this situation, because I get 'illumination lag' when I rotate light source (let's say counterclockwise) and object receiving light (clockwise). This only happens in shader version 3.0 and with light calculations, you provided in this thread.

This is what I currently do:
Code
VS {
	....
	outcolor = (vecAmbient * vecLight) .... blahblah
	for(i=0; i<8; i++)
	{
		outcolor.rgb += calculated light
	}
	...
}

PS {
	...
	float4 textureColor = tex2d
	Incolor.rgb *= textureColor;
	...
}

You can see how 'lag' looks like here:
Youtube link

Best regards!


Looking for free stuff?? Take a look here: http://badcom.at.ua
Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479211
03/01/20 20:15
03/01/20 20:15
Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
txesmi Offline OP
Serious User
txesmi  Offline OP
Serious User

Joined: Jun 2007
Posts: 1,322
Hiporope and its pain
I have no idea what is happening there... I'm stunned, ashtonished crazy

edited______

As I understand, there is no difference in the static table of the effect because of the shader model version but its size. The rendering is an instant action, each draw call draws the render target inmediately, there is not relation between frames. I can't understand how can be possible that the lights have a different position relative to the shader model version.

The only thing I can think on is that you are using other surface position but the world position, such the incoming vertex position in object space. But it should fail in both shader model versions...

Last edited by txesmi; 03/01/20 20:33.
Re: [SUB] compute the conic shape for SPOTLIGHT dyn lights [Re: txesmi] #479212
03/01/20 21:05
03/01/20 21:05
Joined: May 2009
Posts: 5,265
Caucasus
3run Online
Senior Expert
3run  Online
Senior Expert

Joined: May 2009
Posts: 5,265
Caucasus
I'm pretty sure this is all happening due to my crooked hands...
Here you can see the whole .fx file (can compile):
Code
#include <acknex.h>
#include <default.c>

#define PRAGMA_PATH "%EXE_DIR%\samples";

MATERIAL* mtl_test =
{
	effect = "
	
	float4x4 matWorldViewProj;
	float4x4 matWorld;
	float4 vecViewPos;

	float iLights;
	float4 vecLightPos[8];
	float4 vecLightColor[8];
	float4 vecLightDir[8];

	float4 vecLight;
	float4 vecColor;
	float4 vecAmbient;
	float4 vecEmissive;
	
	float4 vecFog;
	float4 vecFogColor;

	texture entSkin1;

	sampler ColorSampler = sampler_state
	{
		Texture = <entSkin1>;
		Mipfilter = None;
		Minfilter = None;
		Magfilter = None;
	};

	struct vertexOut
	{
		float4 Pos : POSITION;
		float4 Color : COLOR0;
		float3 Normal : NORMAL;
		float4 Tex : TEXCOORD0;
		float4 WorldPos : TEXCOORD1;
	};

	vertexOut VS (
	in float4 inPos : POSITION, 
	in float3 inNormal : NORMAL,
	in float2 inTex1 : TEXCOORD0,
	in float2 inTex2 : TEXCOORD1
	)
	{
		inPos.w = 1.0f;	
		vertexOut Out;
		Out.Pos = mul(inPos, matWorldViewProj);
		Out.Normal = normalize (mul(inNormal, (float3x3)matWorld));
		Out.Tex.xy = inTex1.xy;
		Out.Tex.zw = inTex2.xy;
		Out.WorldPos = mul(inPos, matWorld);
		Out.Color = (vecAmbient * vecLight) + float4(vecEmissive.xyz * vecColor.xyz, vecLight.w);
		
		// multiple dynamic lights
		float light = 0;
		for(light = 0; light < iLights; light += 1.0)
		{
			// light ray
			float3 ray = Out.WorldPos - vecLightPos[light].xyz;
			
			// spotlight factor
			float depth = saturate(dot(vecLightDir[light].xyz, ray) / vecLightPos[light].w);
			float spot = 1.0 - saturate(length(cross(vecLightDir[light].xyz, ray)) / (vecLightPos[light].w * depth));
			
			// normalize the light ray
			float dist = length(ray);
			ray /= dist; 
			
			// attenuation
			float att = 1.0 - saturate(dist / vecLightPos[light].w);
			
			// final light factor
			float strength = vecLightDir[light].w ? spot * att : att;
			
			// diffuse term
			Out.Color.rgb += vecLightColor[light].rgb * saturate(-dot(ray, inNormal.xyz)) * strength;
		}
		
		Out.Color.rgb = saturate(Out.Color.rgb);
		
		return Out;
	}

	float4 PS(vertexOut In) : COLOR0
	{
		float4 textureColor = tex2D(ColorSampler, In.Tex.xy);
		In.Color.rgb *= textureColor.rgb;
		
		float fDepth = distance(vecViewPos.xyz, In.WorldPos.xyz);
		float Fog = saturate((fDepth - vecFog.x) * vecFog.z);
		In.Color.rgb = lerp(In.Color.rgb, vecFogColor, Fog);
		In.Color.a = 1;
		
		return In.Color;
	}

	technique model
	{
		pass one
		{		
			VertexShader = compile vs_3_0 VS(); 
			PixelShader  = compile ps_3_0 PS(); 
		}
	}
	
	";
	flags = PASS_SOLID | ENABLE_VIEW;
}

action act_test()
{
	my->material = mtl_test;
	
	while(my)
	{
		my->pan += 5 * time_step;
		wait(1);
	}
}

action act_pointlight()
{
	set(my, UNLIT | BRIGHT);
	my->ambient = 100;
	vec_fill(&my->scale_x, 0.25);
	my->lightrange = 128;
	vec_set(&my->blue, COLOR_WHITE);
	vec_set(&my->skill1, &my->x);
	
	while(my)
	{
		my->x = my->skill1 + fsin(total_ticks * 7, 64);
		my->y = my->skill2 + fcos(total_ticks * 7, 64);
		wait(1);
	}
}

void main()
{
	warn_level = 6;
	fps_max = 60;
	mouse_pointer = 0;
	
	video_mode = 10;
	
	level_load("");
	wait(3);
	
	camera->arc = 90;
	camera->clip_far = 1024;
	camera->clip_near = 0.1;
	
	camera->fog_start = 512;
	camera->fog_end = 1024;
	
	fog_color = 4;
	vec_set(&d3d_fogcolor4.blue, vector(128, 128, 128));
	vec_set(&sky_color.blue, &d3d_fogcolor4.blue);
	
	vec_set(&camera->x, vector(-56, 18, 38));
	vec_set(&camera->pan, vector(338, -30, 0));
	
	ent_create("blob.mdl", nullvector, act_test);
	ent_create(CUBE_MDL, nullvector, act_pointlight);
}
Also I just noticed, that the sun doesn't illuminate object correctly.. illuminated (by sun) side of an object rotates with it as if the sun was rotating (pan) around it.. and if I copy the whole light calculations into PS everything works fine.

Edit: also, I would like to ask, why do you use float instead of int for the 'for' loop where you cycle through all lights?

Edit2: it seems to work with default.fx DoLight (but damn.. it doesn't have spotlight support)
Code
// calculate the light attenuation factor
float DoLightFactor(float4 Light,float3 Pos)
{
	float fac = 0.f;
	if (Light.w > 0.f) {    
		float LD = length(Light.xyz-Pos)/Light.w;
		if (LD < 1.f)
		{
			fac = saturate(1.f - LD);
		}
	}
	return fac; // get the distance factor
}

float DoLightFactorN(float4 Light,float3 P,float3 N)
{
	float3 D = Light.xyz-P; // ray pointing from the light to the surface
	float NdotL = dot(N, normalize(D));   // angle between surface and light ray
	
	if (NdotL >= 0.f)
	{
		return 2 * NdotL * DoLightFactor(Light,P);
	}
	else
	{
		return 0.f;
	}
}

float4 DoPointLight(float3 P, float3 N, float4 Light, float4 LightColor)
{
	return LightColor * DoLightFactorN(Light,P,N);
}

float4 DoLight(float3 P, float3 N, int i)
{
	return DoPointLight(P, N, vecLightPos[i], float4(vecLightColor[i].x, vecLightColor[i].y, vecLightColor[i].z, 1)); 
}

Last edited by 3run; 03/01/20 21:35.

Looking for free stuff?? Take a look here: http://badcom.at.ua
Page 1 of 2 1 2

Moderated by  Blink, Hummel, Superku 

Gamestudio download | chip programmers | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1