Godot Version
Godot 4.3
Question
So i wanted to create a equirectangular view using 6 cameras forming a cube like this:
and a shader that will stich output from those cameras into a equirectangular view that can be for example shown as a 360 image or a video. But i can’t find any resources or tutorials that will explain how to do that.
There is no pipeline to directly get all cameras within one shader. I would create a viewport for each camera. Get an image from each viewport. Then pass the images into a shader to stich them together.
1 Like
Every camera already has a viewport, the problem is stiching them together.
I dont have a lot of experience on how the images should be segmented and distorted into the final texture. but my naive response would be to segment the view-port into regions on the 2D plane and test each fragment location with the region then apply a sampled pixel from the correct camera image.
to make your life easier i think the cameras can do 180 fov, so at most you need to do is a 3 region/image setup. (depending on the default vertical FOV).
but it all kind of depends on the output format you want. I think that detail can guide you. Equirectangular projection - Wikipedia
Did you write a sampler2D
uniform to your shader? Can you assign that shader parameter a viewport texture?
I finally got it working! And I did that by writing a python script with help of ChatGPT that will take 6 screenshots from that cameras in the game and turned it into a equirectangular view and then i replicated this code into Godot shader language, then i applied this shader to a color rect under a SubViewport, that way i have a SubViewport with the 360 degree view of my game.
Here is the code if someone needed it (it may not be beautiful or optimized but it works):
shader_type canvas_item;
uniform sampler2D tex_front;
uniform sampler2D tex_back;
uniform sampler2D tex_left;
uniform sampler2D tex_right;
uniform sampler2D tex_top;
uniform sampler2D tex_bottom;
void fragment() {
float yaw = UV.x * 2.0 * PI - PI;
float pitch = UV.y * PI - PI / 2.0;
pitch *= -1.0;
float x = cos(pitch) * sin(yaw);
float y = sin(pitch);
float z = cos(pitch) * cos(yaw);
float abs_x = abs(x);
float abs_y = abs(y);
float abs_z = abs(z);
// 1 = front, 2 = left, 3 = right, 4 = back, 5 = top, 6 = bottom
int face;
float u;
float v;
if (abs_x >= abs_y && abs_x >= abs_z) {
if (x > 0.0) {
face = 3; // right
}
else {
face = 2; // left
}
if (x > 0.0) {
u = (-z / abs_x + 1.0) * 0.5;
}
else {
u = (z / abs_x + 1.0) * 0.5;
}
v = ((-y / abs_x) + 1.0) * 0.5;
}
else if (abs_y >= abs_x && abs_y <= abs_z) {
if (y > 0.0) {
face = 5; // top
}
else {
face = 6; // bottom
}
u = ((x / abs_y) + 1.0) * 0.5;
if (y > 0.0) {
v = (z / abs_y + 1.0) * 0.5;
}
else {
v = (-z / abs_y + 1.0) * 0.5;
}
}
else {
if (z > 0.0) {
face = 1; // front
}
else {
face = 4; // back
}
u = ((x / abs_z) + 1.0) * 0.5;
v = ((-y / abs_z) + 1.0) * 0.5;
if (face == 4) { // back
u = 1.0 - u;
}
}
if (face == 1) { // front
COLOR = texture(tex_front, vec2(u, v));
} else if (face == 2) { // left
COLOR = texture(tex_left, vec2(u, v));
} else if (face == 3) { // right
COLOR = texture(tex_right, vec2(u, v));
} else if (face == 4) { // back
COLOR = texture(tex_back, vec2(u, v));
} else if (face == 5) { // top
COLOR = texture(tex_top, vec2(u, v));
} else if (face == 6) { // bottom
COLOR = texture(tex_bottom, vec2(u, v));
}
}
Note that it excpects that SubViewport textures have a aspect ratio of 1:1 and the output of the shader is 2:1.