vecLightViewPos question

Posted By: 3run

vecLightViewPos question - 05/12/20 23:29

Hi!

I would like to know more about vecLightViewPos[8], manual just doesn't say anything about it... I'm trying to get the position of each light via pp shader, and thought, that I need to get position relative to the view coordinates, so that's why I'm trying to learn more about vecLightViewPos. I thought that it's float3 with x, y being the screen coordinates and z the lightrange... but this doesn't work, then I tried to use it as a float4 with .w being the lightrange (as for vecLightPos), but this doesn't work either... What would you guys suggest, what am I doing wrong?
Posted By: txesmi

Re: vecLightViewPos question - 05/13/20 08:29

Hi,
'view space' is a Carthesian three dimensional coordinate system like 'world space', but aligned with the position and rotation of a camera. In d3d9, the X axe goes forward on the view direction, Y up, and Z to the left.

worldSpacePos * matView -> viewSpacePos // it had an error, fixed.
viewSpacePos * matViewInv -> worldSpacePos







Posted By: 3run

Re: vecLightViewPos question - 05/13/20 10:42

In this case, I should get lightrange from the vecLightPos.w. As for the position of the light in screen coordinates, vecLightViewPos doesn't help, I guess I need to convert vecLightPos.xyz to screen pos? Already tried oPos = mul(iPos,matWorldViewProj); but that didn't help either.

Edit: I found this algorithm to convert from cartesian to screen:
Code
screenX = cartX + screen_width/2
screenY = screen_height/2 - cartY

// converted it into 
float pos_x = vecLightViewPos[i].x + vecViewPort.x / 2;
float pos_y = vecViewPort.y / 2 - vecLightViewPos[i].y;


It already looks a bit more closer to what I want to archive, but it looks like positions aren't correct when close to the light sources...
[Linked Image]
But on distance it's almost what I want to archive.
[Linked Image]
Posted By: txesmi

Re: vecLightViewPos question - 05/13/20 12:33

The only way to get correct screen coordinates is throgh the normalized device coordinate system (NDC) which can be computed by multiplying object space coordinates by matWorldViewProj, or world space coordinates by matViewProj, or view space coordinates by matProj, since the projection of the camera is determinant.

Code
float2 normalizedProjectionCoordinatesXY = mul(float4(vecLightPos[i].xyz, 1.0f), matViewProj).xy; // range: -1 <-> 1
// or, with the same price and same result:
// float2 normalizedProjectionCoordinatesXY = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj).xy;

float2 screenCoordinates = (0.5f + normalizedProjectionCoordinatesXY * 0.5f) * vecViewport.xy; // range: 0 <-> screen_size - 1


As far as I understand, the radius of the light can't be directly tranformed to screen cordinates as easy as common coordinates. It will need to compute a position in the desired coordinate system, transform it to NDC, compute the resulting radius of the projection and use it with light position screen coordinates to draw the projected circle of the light sphere.

Code
float2 normalizedProjectionCoordinatesXY = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj).xy;
float normalizedProjectionCoordinatesOfOuterPointOfLightSphereX = mul(float4(vecLightViewPos[i].xy, vecLightViewPos[i].z - vecLightViewPos[i].w, 1.0f), matProj).x ; // doing the same in world space would need far more computations
float projectedLightRadiusInScreenPixels = vecViewPort.x * (normalizedProjectionCoordinatesOfOuterPointOfLightSphereX - normalizedProjectionCoordinatesXY.x) / 2.0f;


Notice that in order to compute the projected light radius, the original light radius is subtracted to the view space Z coordinate of the light, which is the inversed X coordinate in screen space. View space YZ plane is parallel to the camera projection plane, so it is proportional to the distance according to the projection.

Salud!

DISCLAIMER: I did not test any of the code above, I might had failed but I am pretty sure it is correct.
Posted By: 3run

Re: vecLightViewPos question - 05/13/20 13:36

I tried out the following:
Code
float2 normalizedProjectionCoordinatesXY = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj).xy;
float normalizedProjectionCoordinatesOfOuterPointOfLightSphereX = mul(float4(vecLightViewPos[i].xy, vecLightViewPos[i].z - vecLightViewPos[i].w, 1.0f), matProj).x;
float projectedLightRadiusInScreenPixels = vecViewPort.x * (normalizedProjectionCoordinatesOfOuterPointOfLightSphereX - normalizedProjectionCoordinatesXY.x) / 2.0f;
float2 screenCoordinates = (0.5f + normalizedProjectionCoordinatesXY * 0.5f) * vecViewPort.xy;
So I set position to screenCoordinates.xy and range to projectedLightRadiusInScreenPixels, but screen is plain dark grin
Tried out matWorldViewProj and matViewProj instead of matProj, but no changes... Also, does vecLightViewPos[i].w contain lightrange?

Edit: this is what I want to archive at the end
[Linked Image]
Posted By: Evo

Re: vecLightViewPos question - 05/13/20 16:04

I wish I could help, but I don't have enough knowledge of shaders to be of assistance. I've also been trying to achieve a similar result with pure 2d and panels, (without 3d). My original concept was to apply a shader to a single black panel that is the same size as the 2d level. Then somehow cut out a feathered circle around the player as well as around all light panels that are placed in the 2d level. But I've recently had no luck in getting it to work correctly.
Posted By: 3run

Re: vecLightViewPos question - 05/13/20 16:21

Thank you for reply Evo! Unfortunately, I'm also not having enough knowledge to be able to get such simple stuff working... :<
If only I could, I would be happy to get this or those shadow shaders I've converted from shadertoy working with acknex and 3d/2d scenes.
Posted By: txesmi

Re: vecLightViewPos question - 05/13/20 16:44

mm, I am sorry, I commited three faults.

1. The normalized device coordinates are computed by dividing the result of the multiply by matProj (or so) by its w component. The raw result of the multiply is known to be in 'clip space'.
2. The y component of the result must be inversed in order to get {0, 0} at the top left corner of the screen.
3. Surprisingly, normalized device coordinates need to be divided by 4 to be in a 0<->1 range. This broke my knowledge, it has always been in a -1<->1 range... shocked

Code
float4 clipSpaceCoordinates = mul(float4(vecLightPos[i].xyz, 1.0f), matViewProj);
float2 normalizedProjectionCoordinatesXY = clipSpaceCoordinates.xy / clipSpaceCoordinates.w;
float2 normalizedScreenCoordinates;
normalizedScreenCoordinates.x = (1.0f + normalizedProjectionCoordinatesXY.x) / 4.0f;
normalizedScreenCoordinates.y = (1.0f - normalizedProjectionCoordinatesXY.y) / 4.0f;
float2 screenCoordinates = normalizedScreenCoordinates * vecViewPort.xy;


A PP effect needs the CHILD flag so it inherits the transformation matrices and light list from the previous stage.

Here goes an example that computes the normalized screen coordinates of a rendered entity:

Code
#include <acknex.h>
#include <default.c>

MATERIAL *mtlNormalizedScreenCoords =
{
	effect = "
		float4x4 matWorldViewProj;
	
		void VS (
			in  float4 inPos     : POSITION,
			out float4 outPos    : POSITION,
			out float4 outColor0 : COLOR0)
		{
			outPos = mul(inPos, matWorldViewProj);
			outColor0.r = (1.0f + outPos.x / outPos.w) / 4.0f;
			outColor0.g = (1.0f - outPos.y / outPos.w) / 4.0f;
			outColor0.b = 0.0;
			outColor0.a = 1.0;
		}
	
		technique
		{
			pass
			{
				VertexShader = compile vs_2_0 VS();
			}
		}
	";
}

void main()
{
	wait(1);
	level_load(SPHERE_MDL);
	camera->material = mtlNormalizedScreenCoords;
	camera->x = -30;
	
	while(!key_esc)
	{
		if(proc_status(def_move) == 0)
		{
			camera->pan = clamp(camera->pan + mickey.x * 0.2, -30, 30);
			camera->tilt = clamp(camera->tilt + mickey.y * 0.2, -20, 20);
		}
		wait(1);
	}
	
	sys_exit(NULL);
}


Posted By: 3run

Re: vecLightViewPos question - 05/13/20 18:21

Thank you for examples, txesmi! I have a question.
Code
float3 clipSpaceCoordinates = mul(float4(vecLightPos[i].xyz, 1.0f), matViewProj).xy;
float2 normalizedProjectionCoordinatesXY = clipSpaceCoordinates.xy / clipSpaceCoordinates.w;
clipSpaceCoordinates is a float3, but you use it's clipSpaceCoordinates .w in normalizedProjectionCoordinatesXY calculation.
Also, in clipSpaceCoordinates itself you only set .xy while calculating it.
So in order to compile, I've changed float3 clipSpaceCoordinates to float4, but it doesn't work.
Also, I've set CHILD flag for the pp view.

EDIT:
Also in
Code
normalizedScreenCoordinates.y = (1.0f - normalizedProjectionCoordinatesXY.y) / 4.0f;
You used
Code
normalizedProjectionCoordinatesXY.x
Instead of Y is that intentional?

EDIT2: ok, I see that you fixed the previous post, but it still doesn't work for some reason...
This is the code I tried out (tried both vecLightViewPos and vecLightPos).
Code
float4 clipSpaceCoordinates = mul(float4(vecLightViewPos[i].xyz, 1.0f), matViewProj);
float2 normalizedProjectionCoordinatesXY = clipSpaceCoordinates.xy / clipSpaceCoordinates.w;
float2 normalizedScreenCoordinates;
normalizedScreenCoordinates.x = (1.0f + normalizedProjectionCoordinatesXY.x) / 4.0f;
normalizedScreenCoordinates.y = (1.0f - normalizedProjectionCoordinatesXY.y) / 4.0f;
float2 screenCoordinates = normalizedScreenCoordinates * vecViewPort.xy;

float normalizedProjectionCoordinatesOfOuterPointOfLightSphereX = mul(float4(vecLightViewPos[i].xy, vecLightViewPos[i].z - vecLightViewPos[i].w, 1.0f), matProj).x;
float projectedLightRadiusInScreenPixels = vecViewPort.x * (normalizedProjectionCoordinatesOfOuterPointOfLightSphereX - normalizedProjectionCoordinatesXY.x) / 2.0f;
Posted By: txesmi

Re: vecLightViewPos question - 05/13/20 18:42

No, sorry, you are right. Both things should be as you say. float4 clip coors and y component. I am pretty absent-minded today. I wanted to give you an answer before get out of home and the hurry defeated me in the original examples code modification. Excuse the inconvenience.
Posted By: 3run

Re: vecLightViewPos question - 05/13/20 18:52

Originally Posted by txesmi
I am pretty absent-minded today.
I'm absent-minded always grin No inconvenience at all! Thank you for helping out, without you forum is completely dead (especially when it comes to shaders).
Posted By: 3run

Re: vecLightViewPos question - 05/13/20 19:54

Ok, so after googling I found this awesome answer on stackoverflow
C++/OpenGL convert world coords to screen(2D) coords

It explains that you need to:
Quote
- transform into clip-space
- then transform from clip-space to normalized device coordinate space (NDC space)
- and then transform from this [-1, 1] (NDC) space to window-relative coordinates!

And from there I've got this code
Code
vec4 clipSpacePos = projectionMatrix * (viewMatrix * vec4(point3D, 1.0));
vec3 ndcSpacePos = clipSpacePos.xyz / clipSpacePos.w;
vec2 windowSpacePos = ((ndcSpacePos.xy + 1.0) / 2.0) * viewSize + viewOffset; // OpenGL bottom-left of the window
vec2 windowSpacePos = vec2(((ndcSpacePos.x + 1.0) / 2.0) * viewSize.x + viewOffset.x, ((1.0 - ndcSpacePos.y) / 2.0) * viewSize.y + viewOffset.y); // top-left
Which I've later on converted into hlsl
Code
float4 clipSpacePos = mul(float4(vecLightPos[i].xyz, 1.0f), matViewProj);
float3 ndcSpacePos = clipSpacePos.xyz / clipSpacePos.w;
float2 windowSpacePos = float2(((ndcSpacePos.x + 1.0) / 2.0) * vecViewPort.x, ((1.0 - ndcSpacePos.y) / 2.0) * vecViewPort.y);
And at the end it worked like a charm!
[Linked Image]


Thank you all for replies! Next step is getting fog of war/lights working! grin
Posted By: 3run

Re: vecLightViewPos question - 05/13/20 21:26

So this is how I got it working so far (in motion it 'flickers')
[Linked Image]

.fx file (if anyone needs it):
Code
//source
//https://www.shadertoy.com/view/ttj3zd

float4x4 matViewProj;
const float4 vecTime;
const float4 vecViewPort;

float lightRangeFactor = 1.0;
float smoothObjectPadding = 0.33;
float lightSaturationFactor = 0.2;
float flickerSpeedFactor = 2;

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

texture TargetMap;

sampler postTex = sampler_state
{
	texture = <TargetMap>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU = Clamp;
	AddressV = Clamp;
};

float4 FP(float2 fragCoord: VPOS) : COLOR
{
	float2 uv = fragCoord.xy / vecViewPort.xy;
	float3 color = tex2D(postTex, uv).rgb;
	float3 blurColor = float3(0.0, 0.0, 0.0);
	
	float aspectRatio = vecViewPort.x / vecViewPort.y;
	
	float i = 0;
	for(i = 0; i < iLights; i += 1.0)
	{
		// pointlights only
		if(vecLightDir[i].x <= 0 && vecLightDir[i].y <= 0 && vecLightDir[i].z <= 0)
		{
			float4 clipSpacePos = mul(float4(vecLightPos[i].xyz, 1.0f), matViewProj);
			float3 ndcSpacePos = clipSpacePos.xyz / clipSpacePos.w;
			float2 windowSpacePos = float2(((ndcSpacePos.x + 1.0) / 2.0) * vecViewPort.x, ((1.0 - ndcSpacePos.y) / 2.0) * vecViewPort.y);
			
			float2 objectCenter = windowSpacePos.xy / vecViewPort.xy;
			
			float2 v = uv - objectCenter;
			v.x = v.x * aspectRatio;
			float flickerTime = sin(vecTime.w * flickerSpeedFactor);
			
			float min_size = (vecLightPos[i].w * lightRangeFactor / 2.0) / 100;
			float max_size = (vecLightPos[i].w * lightRangeFactor / 2.1) / 100;
			float size = lerp(min_size, max_size, flickerTime);
			
			float smoothSize = size * smoothObjectPadding;
			float circleMix = smoothstep(size, size - smoothSize, length(v));
			
			blurColor.rgb = lerp(blurColor.rgb, color.rgb + (vecLightColor[i].xyz * lightSaturationFactor), circleMix);
		}
	}
	
	float4 fragColor = float4(blurColor, 1.0);
	return fragColor;
}

technique
{
	pass one
	{
		PixelShader = compile ps_3_0 FP();
	}
}


The only thing left is, to change lightrange depending on the distance to the light source. If you guys have any ideas, please let me know.
Couldn't get this one to work properly...
Code
float2 normalizedProjectionCoordinatesXY = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj).xy;
float normalizedProjectionCoordinatesOfOuterPointOfLightSphereX = mul(float4(vecLightViewPos[i].xy, vecLightViewPos[i].z - vecLightViewPos[i].w, 1.0f), matProj).x ; // doing the same in world space would need far more computations
float projectedLightRadiusInScreenPixels = vecViewPort.x * (normalizedProjectionCoordinatesOfOuterPointOfLightSphereX - normalizedProjectionCoordinatesXY.x) / 2.0f;


Best regards!
Posted By: txesmi

Re: vecLightViewPos question - 05/14/20 13:26

Originally Posted by 3run
- transform into clip-space
- then transform from clip-space to normalized device coordinate space (NDC space)
- and then transform from this [-1, 1] (NDC) space to window-relative coordinates!

The very same I said wink

Originally Posted by 3run
does vecLightViewPos[i].w contain lightrange?


I thought yes but no and it should frown
I can only name it a bug because it is forcing to include full world space light vectors to get the light ranges while the W component of vecLightViewPos is empty. brff...

Here goes an example of the concepts I tryed to explain but with no bugs grin
Code
#include <acknex.h>
#include <default.c>

MATERIAL *mtlProjectedLights =
{
	effect = "
		float4x4 matProj;
		const float4 vecViewPort;
		
		float iLights;
		float4 vecLightPos[8];
		float4 vecLightViewPos[8];
		float4 vecLightDir[8];
		float4 vecLightColor[8];
		
		texture TargetMap;
		
		sampler postTex = sampler_state
		{
			Texture = <TargetMap>;
			MinFilter = Point; // No need of linear interpolation on pixel to pixel pp effects. 
		};
		
		float4 PS (
			in float2 inTex : TEXCOORD0, 
			in float2 inPos : VPOS) : COLOR0
		{
			float4 color0 = float4(0, 0, 0, 1.0f);
			
			for(float i = 0; i < iLights; i += 1.0f)
			{
				if(vecLightDir[i].w > 0) // Avoid spotlights
					continue;
				
				float4 clipSpacePos = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj);
				float2 ndcSpacePos = clipSpacePos.xy / clipSpacePos.w;
				float2 windowSpacePos = float2(1.0f + ndcSpacePos.x, 1.0f - ndcSpacePos.y) * vecViewPort.xy * 0.5f;
				
				float4 clipSpaceOutterPoint = mul(float4(vecLightViewPos[i].x - vecLightPos[i].w, vecLightViewPos[i].yz, 1.0f), matProj);
				float ndcSpaceOutterPoint = clipSpaceOutterPoint.x / clipSpaceOutterPoint.w;
				
				float windowSpaceRadius = (ndcSpacePos.x - ndcSpaceOutterPoint) * vecViewPort.x * 0.5f;
				color0.rgb += vecLightColor[i].rgb * saturate((1.0f - length(inPos - windowSpacePos) / windowSpaceRadius));
			}
			
			float3 albedo = tex2D(postTex, inTex).rgb;
			color0.rgb = albedo * saturate(color0.rgb);
			
			return color0;
		}
		
		technique
		{
			pass one
			{
				ZWriteEnable = False;
				AlphaBlendEnable = False;
				PixelShader = compile ps_3_0 PS();
			}
		}	
	";
}

VIEW *camProjectedLights =
{
	material = mtlProjectedLights;
	flags = PROCESS_TARGET | CHILD | SHOW;
}

void main () {
	video_mode = 10;
	sun_light = 0;
	wait(3);
	level_load("level.wmb");
	camera->arc = 100;
	camera->stage = camProjectedLights;
	
	while(!key_esc)
		wait(1);
	
	sys_exit(NULL);
}

#define light_blue skill1
#define light_green skill2
#define light_red skill3
#define light_range skill4
//uses: light_blue, light_green, light_red, light_range
action actLight () {
	my->blue = my->light_blue;
	my->green = my->light_green;
	my->red = my->light_red;
	my->lightrange = my->light_range;
	
	vec_set(&my->skill20, &my->x);
	my->skill23 = random(360);
	
	my->flags |= UNLIT | LIGHT | BRIGHT;
	
	while(1)
	{
		my->lightrange = my->light_range + random(10); // flicker
		my->x = my->skill20 + fsin(my->skill23 + total_ticks * 4, 80);
		my->y = my->skill21 + fcos(my->skill23 + total_ticks * 4, 80);
		wait(1);
	}
}


Salud!
Posted By: 3run

Re: vecLightViewPos question - 05/14/20 18:33

Material works like a charm! laugh Thank you txesmi!

I've implemented some of your code into the one that I had yesterday and I'm pretty happy with results!

Here how it looks like
[Linked Image]

Here is standalone demo
Code
#include <acknex.h>
#include <default.c>

VIEW *pp_view = { layer = 2; flags = PROCESS_TARGET | CHILD; }

MATERIAL *mtl_2d_lights =
{
	effect = "
	//https://www.shadertoy.com/view/ttj3zd

	float4x4 matProj;
	const float4 vecTime;
	const float4 vecViewPort;

	float smoothObjectPadding = 0.33;
	float flickerSpeedFactor = 2;

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

	texture TargetMap;

	sampler postTex = sampler_state
	{
		texture = <TargetMap>;
		MinFilter = Point;
	};

	float4 FP(
	in float2 inTex : TEXCOORD0, 
	in float2 inPos : VPOS) : COLOR
	{
		float3 color = tex2D(postTex, inTex).rgb;
		float3 blurColor = float3(0.0, 0.0, 0.0);
		
		float i = 0;
		for(i = 0; i < iLights; i += 1.0)
		{
			// pointlights only
			// checking only .w doesn't help to skip the sun
			if(vecLightDir[i].x <= 0 && vecLightDir[i].y <= 0 && vecLightDir[i].z <= 0)
			{
				float4 clipSpacePos = mul(float4(vecLightViewPos[i].xyz, 1.0f), matProj);
				float2 ndcSpacePos = clipSpacePos.xy / clipSpacePos.w;
				float2 windowSpacePos = float2(1.0f + ndcSpacePos.x, 1.0f - ndcSpacePos.y) * vecViewPort.xy * 0.5f;
				float4 clipSpaceOutterPoint = mul(float4(vecLightViewPos[i].x - vecLightPos[i].w, vecLightViewPos[i].yz, 1.0f), matProj);
				float ndcSpaceOutterPoint = clipSpaceOutterPoint.x / clipSpaceOutterPoint.w;
				float windowSpaceRadius = (ndcSpacePos.x - ndcSpaceOutterPoint) * vecViewPort.x * 0.5f;
				
				float2 objectCenter = windowSpacePos.xy / vecViewPort.xy;
				float2 uv = length(inPos - windowSpacePos);
				float2 pos = uv - objectCenter;
				
				float flickerTime = sin(vecTime.w * flickerSpeedFactor);
				float min_size = windowSpaceRadius;
				float max_size = windowSpaceRadius + 8;
				float size = lerp(min_size, max_size, flickerTime);
				
				float smoothSize = size * smoothObjectPadding;
				float circleMix = smoothstep(size, size - smoothSize, length(pos));
				
				blurColor.rgb = lerp(blurColor.rgb, color.rgb * vecLightColor[i].rgb, circleMix);
			}
		}
		
		float4 fragColor = float4(blurColor, 1.0);
		return fragColor;
	}

	technique
	{
		pass one
		{
			ZWriteEnable = False;
			AlphaBlendEnable = False;
			PixelShader = compile ps_3_0 FP();
		}
	}	
	";
}

action act_light()
{
	set(my, PASSABLE);
	vec_set(&my->blue, vector(128 + random(128), 128 + random(128), 128 + random(128)));
	my->lightrange = 128;
}

void main()
{
	video_mode = 10;
	warn_level = 6;
	sun_light = 0;
	level_load("");
	
	random_seed(0);
	camera->arc = 90;
	
	vec_set(&camera->x, vector(0, 300, 300));
	vec_set(&camera->pan, vector(270, -45, 0));
	
	pp_view->material = mtl_2d_lights;	
	camera->stage = pp_view;	
	
	ENTITY *ent = ent_create(CUBE_MDL, nullvector, NULL);
	vec_set(&ent->scale_x, vector(64, 64, 0.01));
	c_setminmax(ent);
	
	int i = 0;
	for(i = 0; i < 3; i++)
	{
		ent_create(CUBE_MDL, vector(-128 + (128 * i), 0, 16), act_light);
	}
}

There is another thing I would like to implement, but yet have no idea how.
It would be cool to mix the light colors on the 'contact' edges.
Something like this
[Linked Image]


Greets
Posted By: txesmi

Re: vecLightViewPos question - 05/15/20 11:58

Glad of been of help laugh

That color space is called CMY (cyan, magenta, yellow) and it is the inverse of RGB when normalized.
Code
RGB = 1 - CMY;
CMY = 1 - RGB;


They are both additive/sustractive color spaces so you can operate same. The only problem is that CMY does not work as a 0<->1 brightness factor as RGB does in color multiply because the components content is inverse to its brightness.
© 2024 lite-C Forums