📜 ⬆️ ⬇️

Volumetric planets in 2D via shader

Do you remember how you asked me to write about shaders? Remember? Not? But I remember and even wrote. You are welcome, let's talk about the beautiful.

Today, I will talk about how I did three-dimensional rotating planets for our game blast-off. Ie, of course, they are completely flat, just a couple of triangles, but they look like voluminous ones.


')
Interested? I ask under the cat. Pictures decently.

Introductory text


It’s not for me to tell you about how the cinema and games cheat the spectators and players, forcing them to see what they need, and not what they really are. Special effects are not always, but rather the opposite, rarely, done according to all the rules and laws of physics and nature. Everywhere there are simplifications, tricks or optical illusion. On the other hand, we have technical limitations, and spectators fail - not everyone can imagine what a real black hole looks like or a real helicopter explosion from an anti-aircraft missile, for example.

What is it for me? Oh yeah, my effects, too, do not claim to be the most honest and believable. Strictly speaking, I absolutely do not care how it will be correct, so long as it looks cool! When developing this or that effect, I am more guided by the ratio of its quality (whether I like it or not), as well as the speed of its work and complexity of execution. I say this not least in order to avoid disputes. Perhaps, if I did not give explanations and source code, you would hardly have noticed my deception "by eye".

In general, many are wondering - “why don't you hire an artist?” Or “why not 3D?”. Well, seriously, well, why? We are adults and we know that indie developers make games for fun and make them painstakingly. Grooming and Lileyat. And of course, they love bicycles very much. I am not an exception, I love them too and periodically do them. There would have to be a tirade about “pseudo-indie” developers disguising as independent and trying to cut the jackpot, but I’ll omit it. So, you create the world, you create what you want and how you want. You are responsible for each pixel in the final image and only you are able to make it the way it should be!

2D full length


Today, I will talk about how I did three-dimensional rotating planets for our game blast-off. Ie, of course, they are completely flat, just a couple of triangles, but they look like voluminous ones. Our game is completely two-dimensional, so there are not many options for implementing this effect. The option to prerenderit many frames for each planet, and then change frame by frame, as it was done in old games, can be cut off. It is cumbersome and does not allow making, for example, dynamic clouds, thunderstorms on the surface of the planet or changing the direction of illumination. Ie, of course, gives, but for this it is necessary for each type of planet to do just a bunch of sprites. Of course, this is not acceptable.

The decision was obvious. You need to do everything on the fly, a shader. Many immediately have the thought “well, obviously, to yourself! Obviously - this is doing 3D for the 3D planet, isn't it? ”. Not. Because the game we have is only two-dimensional. The only question is how. Changing the texture geometry reliably is a rather cumbersome code, only if you do not use the displacement map. With the displacement map, the code becomes simpler and, as a result, much faster.

Speaking of the displacement map, since it contains vectors, and each color component is only 8 bits, it’s easy to calculate that we can encode displacements by no more than 128 pixels. Of course, we can take into account that each gradation is 2 pixels, and then we will be able to encode offsets up to 256 pixels in each direction. However, in our case, we had enough of the size of an ordinary displacement map. I compensated for the increase in size by linear filtering in the shader.
Here it is necessary to make a reservation that, at the moment, my quad engine engine only supports A8R8G8B8 textures.

Conveyor


The shader will have to distort not only the surface of the planet, but also clouds, shadows, luminescence and everything that you want to use when drawing the planet. Those same distortions that are not visible to the eye can be easily noticed by replacing the texture of the planet with a grid-like texture.

Further steps will cause the atmosphere and shading the planet. This can be done without a shader, however, even here a shader can be used, complicating it by entering the light point parameter. I managed a simpler solution with pre-drawn textures. Of course, a planet can be, say, without an atmosphere and without clouds. The planet can break apart and emit light all over its surface, to have geysers and volcanoes, to be covered with cities or technological facilities. Options to select weight.

Optionally, you can work even with the glow of the surface of the planet, or even implement different textures on the day and night parts of the planet. For example, the glow of cities at night.

Some lyrics
When writing this article, an interesting idea appeared. And what if I move the background with the stars and at the same time rotate the planet in the opposite direction, changing the lighting on it? Then there will be a complete illusion of flying around our planet. As it is suggested to me from the back rows, in this case, you need not to forget about the focus and add distortions so that it is not too improbable.


Tools




Shader
float2 Velocity : register(c0); sampler2D DiffuseMap : register(s0); sampler2D DuDvMap : register(s1); float4 std_PS(vertexOutput Input) : COLOR { float2 source = Input.TexCoord; source.y -= 2.0 / 512.0; source.y = max(source.y, 0.0); source.x += 2.0 / 512.0; float4 right = (tex2D(DuDvMap, source) * 2.0) - 1.0; source.x -= 4.0 / 512.0; source.y = max(source.x, 0.0); float4 left = (tex2D(DuDvMap, source) * 2.0) - 1.0; source.y += 4.0 / 512.0; float4 left2 = (tex2D(DuDvMap, source) * 2.0) - 1.0; source.x += 4.0 / 512.0; float4 right2 = (tex2D(DuDvMap, source) * 2.0) - 1.0; float4 middle = right / 4.0 + left / 4.0 + right2 / 4.0 + left2 / 4.0; float2 coord = middle.rg; coord.x = coord.x / 4.0; coord.y = coord.y / 2.0; coord += Velocity; float4 result = tex2D(DiffuseMap, coord); return float4(result.rgb, middle.a * result.a) * Input.Color; } 



From theory to practice


We begin to work with the grid. The grid will allow us to understand how close we come to the result that suits us. We take into account that the length of the texture in width should be twice as long as in height, since the planet has both sides. For the standard, take the texture resolution of 1024x512 pixels.



The second necessary texture will be the same dUdV map with encoded vectors. It is equal to 512x512 points. There are smaller sizes. Why? Because we see only a square piece of the grid, distorted by a shader. So we need to tell the shader what kind of square piece of the grid we give. Gradually changing the square with a shift, we get the effect of rotation.



Superimpose the shader and distort the original mesh texture using the second texture.



The screenshot clearly shows the noisiness of the black lines. They seemed to be covered with pixel waves. Vision does not deceive you and the problem is in the limitations of dUdV texture. The displacement map has only 256 gradations (as I wrote above), that is, 128 plus and minus. This is a limitation of textures in the A8R8G8B8 format. And the size of the texture is 512 pixels. Because of this, the gradient turns out to be stepped, in some places with duplicate pixels. As a result, the picture is distorted as a whole is true, but at the pixel level it contains artifacts. Of course, the size of the planet and its texture allow us to neglect this visual artifact, but it's a shame.

Technical details
In the shader, I tried a little to weaken the effect by taking not a vector for a particular pixel from dUdV maps, but several neighbors and interpolating them. Thus, the averaging value. Since there is no limit to 256 gradations on the video card (all calculations are made with floating point numbers), the result is closer to what you want.

In dUdV map we have not 2, but 3 channels. RGB. Alpha tells the shader about the shape of the planet, smoothing along the edges and is therefore not considered. R and G are used to encode the offset. There is still 1 byte empty. It could be used to encode an additional 4 bits for each color (8 + 4). This would greatly increase the accuracy and avoid any problems with flickering and distortion. Obviously, this is the next step in the development of the effect.

So go ahead! Add shading. This can also be done by a shader, but at this stage it is absolutely not important, so I will simply impose a translucent texture in the subtraction mode:



Change the texture to the surface of the planetary and begin to play already with it:



It is not obvious to everyone what is missing here - the atmosphere. So, as I make a universal class for drawing various types of planets, I will add clouds directly on this one. Clouds are added by the same distortion method as the texture of the planet itself, but in a separate layer, so that the clouds can also be moved and twisted in a different direction than the surface of the planet:



Let's combine the clouds with the existing planet. Make clouds 1.5% wider than the planet, they are not very low. And in the end we get here such indecency:



What happened, and why is everything so bad? There are clouds, you can even notice the difference between the screenshots, but it is so imperceptible that everything is really very bad. But everything is bad because I did not take into account the fact that the planet can be bright and the gray clouds on it can be seen very badly. On the one hand, this is correct, but after all, the clouds cast a shadow on the surface of the planet, but we do not have that. Add another dark cloud layer below this layer.



Now the clouds are clearly visible on both light and dark planets. But if there are clouds, then only one is missing - the atmosphere. We must add it. The atmosphere should create a slight glow on the sunny side, so it needs to be added in the mode of adding color, not mixing.



It is quite another matter.

However, I return to the above, namely, the fact that the class is universal. Let's imagine that the planet is covered with scars, glowing scars. For example, glowing lava, city lights, or, well, I do not know what, think of something your own. And in the air she has not bright clouds, but clouds of black smoke. What you can not do for the beauty of the game? We will generate such a texture for our planet, leaving all the superimposed effects in place:



Here it is. Almost beautiful, but there is one BUT - nothing glows on the dark side of the planet. We pick up a glow map (also generated with the surface texture) and add it as well. It would be logical to add a glow already on top of everything so that no shadows affect the light, right?



No, not true! I would say that it is beautiful and that is necessary, but the clouds (smoke) do not overlap the glow, then the sequence of layers is wrong.

Obviously, you need to add a rounding shader, imposing a shadow on the result, depending on whether the layer is glowing or not. Otherwise, how not to rearrange our layers, the effect of the desired not to get!

Maybe this is Mars? Well, or something like that, but with the atmosphere and a huge airstrip in the middle? No, this is not a bug, as it may seem at first, it is such a texture.



It was here, when it came to something like Mars and Earth, that the question arose that the clouds should be more white for them. No sooner said than done! And without a home planet, a demo, I believe, would not be at all visual. Therefore, let's make the Earth too, what is there!

Well, at the same time it would be necessary to remove the bug that comes out above and below the planet. Let's fix it, it will be more beautiful.



Not the same planets


Let's create the same scale for charging the power gun or mana using the same method. This is like someone more like it. For clarity, cut a piece from the top, we have already spent a little electricity. Be sure to watch the video, as this thing is spinning and looks a little different than in statics.

Diablo3:



Our implementation:



findings


Yes, what is there. Shaders are cool. That's all the conclusions! Seriously, without shaders, making a very beautiful and dynamic game will be like hard labor. Almost all the graphics (except for the surface of the Earth) are generated and require no artist intervention. This allows you to make the game with minimal involvement of people from the outside.

Finally I give the video:

Source: https://habr.com/ru/post/248381/


All Articles