Skip to main content
GameDev.net gamedev.net
🔒 Locked

Black Hole Distortion

Started by Scottehy Nov 21, 2010 at 5:11 AM 15 replies 14k views
Original Post
Scottehy
Scottehy
I'm gathering this should be a really straight forward question, someone with some good shader knowledge should be able to help me here pretty easy I think.

I'm trying to achieve this effect around a black hole in a 3d world.

http://www.genetologisch-onderzoek.nl/wp-content/image_upload/blackhole.jpg

Problem is my knowledge with shaders isn't the best and I'm a little brain dead. What I've setup so far is a full screen shader effect which does nothing as of now, it just displays my renter target to the screen. But I have already created a few things I know I'll need for the shader. Such as - I've converted the black holes position in 3d space to a 2d coordinate on screen, so I know where to technically center the distortion effect. And I have the distance between the camera to the black holes position. With those to values I should be able to create that distortion effect.

So if anyone is kind enough to show me how creating an effect like this could be done in a pixel shader it would be much appreciated.

Thanks a heap,
Scott.
stevenmarky
stevenmarky
Hi Scottehy I created an effect like this a long time ago - check out
">My Video on YouTube
and skip to 1:50 (it is blocked in some countries though).
How it works:
The black hole is a 2D billboarded quad
The whole scene is rendererd back to front so that things can be drawn in front of the black hole (this approach isn't perfect but it works).
In the pixel shader the distance (from the current pixel) to the centre of the black hole is calculated, and this is used to create a refraction effect using the current render buffer as an input. The HLSL uses the refract function (MSDN)

[Edited by - stevenmarky on November 21, 2010 8:50:45 AM]
Scottehy
Scottehy
Thanks for the reply, your video looks pretty much exactly how I want it. But my game is pure procedural, everything in my demo from the nebulae to moons are all procedurally generated. So I'm not to sure how you mean by using a quad, is that quad a normal map which the refract function somehow can check which way to bend the light in a sense? I'm not to worry about rendering order, I can fix that one up later.

Just looking at this refract function I'm not to sure what I'd even really pass into it from what I have.

Thanks :)
Vilem Otte
Vilem Otte
If you would like it to be physically based, you would need to use ray tracing.

The math behind this should be detaily described in General relativity. Although it can be "simulated" with using just a fraction of computational power needed to perform physically based computation.

I think stevenmarky means using billboarded quad with normal map. Refract function in glsl works like this
vec3 refract(vec3 I, vec3 N, float IOR){     float IdotN = dot(I, N);     float k = 1.0 - IOR * IOR * (1.0 - IdotN * IdotN);     if(k < 0.0)     {          return vec3(0.0, 0.0, 0.0);     }     else     {          return (IOR * I - (IOR * IdotN + sqrt(k)) * N;     }}

where I is vector from your eye to the point; N is normal vector of hitpoint and IOR is index of refraction between surfaces.
stevenmarky
stevenmarky
I didn't use a normal map in my situation - a normal map would probably be faster but I calculated my normals instead inside the pixel shader, this allowed me to tweak it and see the results.

This is the code for my black hole shader, keep in mind that it was one of the first shaders I wrote and that this wasn't written to be used in any sort of commercial production. s0 contains the current render buffer texture.

 // IMPORTANT: I switched the PS model to 3_0 (instead of 2_0) so I can use input semantic VPOS // There are alternatives, so this should be reverted back to 2_0 eventually.  sampler2D s0 : register(s0); sampler2D s1 : register(s1);  // Vertex shader input structurestruct PS_INPUT{	float2 pos : VPOS;    float2 tex0 : TEXCOORD0;};struct PS_OUTPUT{    float4 color : COLOR0;  };float2 screenSize: register(c0);PS_OUTPUT main( in PS_INPUT IN ){	PS_OUTPUT OUT;	float2 balanced=IN.tex0 - float2(0.5, 0.5);	float2 normalized = normalize(balanced);	float distance = length(balanced);	// The strength of the gravitational field at this point:	float scaled = distance * 20.0f;	float strength = 1 / ( scaled * scaled );	float3 rayDirection = float3(0,0,1);	float3 surfaceNormal=normalize( float3(normalized, 1.0/strength) ); // normalized	float3 newBeam=refract(rayDirection, surfaceNormal, 2.6);	float2 newPos = IN.pos + float2( newBeam.x, newBeam.y)*100;	OUT.color=tex2D( s0, (newPos )/screenSize); 	OUT.color*=length(newBeam);	OUT.color.a=1.0f;		return OUT;}
Scottehy
Scottehy
Thanks for that reply stevenmarky, couple of things confuse me though. Is float2 pos : VPOS; meant to represent the position of the center of the black hole technically. Or is it meant to represent something else. Also the s1 register is never used?

Thanks :)
stevenmarky
stevenmarky
The float2 pos : VPOS; is in the input to the pixel shader.
For each pixel of the blackhole quad, pos will be the pixel location in screen space.
Scottehy
Scottehy
Your s1 register is never used though, so I'm a little confused on how that exactly maps with the quad, if VPOS is using a position from the quad?
stevenmarky
stevenmarky
I don't use s1, I use s0 - I could delete s1 without any effect.
s0 is bound to the texture of the render buffer.
Scottehy
Scottehy
Alright sweet, thank you a heap. I have it working now. It does need a few tweaks though which I'm a little unsure of as again, I'm not the greatest with shaders.

Heres one of my results, and you'll see the problems :P

BlackHole

As you can see I change my 3d black hole in space to 2d coordinates. Problem is how can I make it so that circle in the center would be square, as obviously I'm using a widescreen resolution. So somehow I need to make the distortion calculate differently. And one last question sorry, how can I change the size of the area of the distortion, so I can base the size of the effect on the distance I am away from the blackhole.

Thank you so much for the help though, this effect looks great.

EDIT:
Changing the scale is no issue, i tweaked with your values thats fine now. But I'm still a little confused on how to make this circular shaped. I took my black holes sphere drawing away so it's just the fullscreen effect. Which makes it look a lot nicer.

BlackHole

[Edited by - Scottehy on November 21, 2010 10:15:07 PM]
Scottehy
Scottehy
If it's any help to anyone this is the current shader code:
uniform extern texture ScreenTexture;    sampler ScreenS = sampler_state{    Texture = <ScreenTexture>;    };struct PS_INPUT{    float2 tex0 : TEXCOORD0;};float2 screenSize;float2 centerCoord;float  distanceFromCam;float4 BlackHolePS( in PS_INPUT IN ) : COLOR0{	float4 color;		float2 balanced = IN.tex0 - centerCoord;	float2 normalized = normalize(balanced);	float distance = length(balanced);		float2 pos = IN.tex0.xy;	pos.x = pos.x * screenSize.x;	pos.y = pos.y * screenSize.y;		// The strength of the gravitational field at this point:	float scaled = distance * distanceFromCam;	float strength = 1 / ( scaled * scaled );	float3 rayDirection = float3(0, 0, 1);	float3 surfaceNormal = normalize( float3(normalized, 1.0 / strength) );	float3 newBeam = refract(rayDirection, surfaceNormal, 2.6);	float2 newPos = pos + float2(newBeam.x, newBeam.y) * 300;	color = tex2D(ScreenS, (newPos) / screenSize); 	color *= length(newBeam);	color.a = 1.0f;		return color;}technique{    pass P0    {        PixelShader = compile ps_2_0 BlackHolePS();    }}

This topic talks about using your view/projection matrix to draw things to there correct size, ( http://www.gamedev.net/community/forums/topic.asp?topic_id=419791 ) anyone know how I can fix this?

Thanks :)
SiS-Shadowman
SiS-Shadowman
Quote:
Original post by Scottehy
But I'm still a little confused on how to make this circular shaped.


I think this is a problem because you're calculating the distortion in relative screen coordinates. Thus a relative distortion of (0.5, 0.5) may shift stuff 10 pixels in height and 20 pixels in width, if you have an aspect ratio of 2:1.
But you want a distortion of (0.5, 0.5) to shift stuff by n pixels in width & height together. Simply divide the x-coordinate of your distortion by the aspect or multiply the y-coordinate. Either way, you should get a spherical black hole instead of an elliptical:

...float2 dist = float2(newBeam.x, newBeam.y) * 300;// Eitherdist.x /= aspect;// Ordist.y *= aspect;float2 newPos = pos + dist;...


And really thanks for sharing your code. The effect looks really nice.
Scottehy
Scottehy
Hmm the moment I read that I thought that would of fixed it. Yet no matter what numbers I use seem to change anything, I'm even trying:

float2 dist = float2(newBeam.x, newBeam.y) * 300;
dist.x /= aspect * 200.0f;
float2 newPos = pos + dist;

Yet even something like this does not alter the elliptical shape what so ever.

Thanks for the reply though, I'll keep tweaking :)
stevenmarky
stevenmarky
When I used the shader I applied it to a perfectly square quad which is probably why I didn't get this issue. Are you applying the shader to a fullscreen quad?

Try dividing balanced.x by the ratio of width/height, e.g.

float2 balanced = IN.tex0 - centerCoord;balanced.x=balanced.x/1.5;float2 normalized = normalize(balanced);float distance = length(balanced);
Scottehy
Scottehy
Hmm i find this very confusing, the reason I asked the question really was because I had already tried this and it didn't work. Yet I tried it this time and it seemed to work a charm. I did this though

balanced.y /= aspect;

Not that it really changes in anyway, but I'm sure I did this before. Either way thank you so much for your help, you've made me understand a great deal more about shaders, thats for sure.

Thank you :)
stevenmarky
stevenmarky
Glad I could help!

Nice looking grid and background by the way.
Scottehy
Scottehy
Thanks :) the planets come out great as well when the noise textures are increased in size and quality, but considering I'm generating sometimes thousands of them it's not necessary to have them looking perfect.

Thank you for all the help you've given, it's really appreciated.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.