Kaleidoskop seperti di masa kecil


Kadang-kadang refleksi di cermin lebih nyata daripada objek itu sendiri ...
- Lewis Carroll (Alice in the looking glass)

Pada usia muda saya punya mainan lucu - kaleidoskop. Selama berjam-jam, saya memeriksa pola yang benar yang terbuat dari pecahan kaca pecah berwarna. Sesuatu yang menyihir ada dalam perenungan meditatif ini. Sekarang, sebagai seorang ayah, saya ingin menunjukkan kepada anak-anak saya keindahan dari konstruksi kekacauan yang tepat.

Anak-anak sekarang modern, mainan biasa kurang menarik bagi mereka, memberi mereka komputer atau tablet. Oleh karena itu, saya ingin membuat kembali prototipe digital dari varian kaleidoskop, dan pada saat yang sama mempraktikkan keterampilan grafis komputer saya.

Saya mengundang Anda untuk terjun bersama saya ke dunia refleksi.

Gagasan orisinalnya adalah membangun model fisik kaleidoskop yang lengkap. Perangkat ini terdiri dari beberapa cermin yang terletak pada sudut satu sama lain diletakkan dalam sebuah tabung. Kaleidoskop dari masa kecil saya terdiri dari tiga cermin, dan saya memutuskan untuk membuat ulang desain ini.

Solusi yang paling jelas bagi saya adalah menggunakan ray tracing. 3 pesawat cermin dibuat pada sudut 120 derajat satu sama lain.



Menempatkan objek di luar ujung cermin dan menggunakan beberapa pantulan ulang sinar (sekitar 20 pantulan) kita mendapatkan kaleidoskop yang berfungsi dengan baik.



Untuk membuat perutean, shader komputasi digunakan. Gambar di-output dalam tekstur, yang kemudian ditampilkan di layar. Spher digunakan sebagai objek gambar sebagai bentuk yang lebih sederhana. Pada kartu video saya dalam mode rendering realtime, saya berhasil mencapai sekitar 20-25 FPS, dan ini hanya dengan tiga objek dan satu sumber cahaya, yang menyedihkan. Saya ingin gerakan kacau dari banyak bentuk yang berbeda, serta sumber pencahayaan waktu nyata, tetapi ini akan menyebabkan perlambatan yang lebih besar.

Setelah beberapa pendekatan untuk optimasi, saya menunda model ini sebagai tidak menjanjikan.

Komputasi Kode Shader GLSL
#version 430 core
layout( local_size_x = 32, local_size_y = 32 ) in;
layout(binding = 0, rgba8) uniform image2D IMG;
layout(binding = 1, std430) buffer InSphere {vec4 Shape_obj[];};
layout(binding = 2, std430) buffer InSphere_color {vec4 Sphere_color[];};

uniform vec2 u_InvScreenSize;
uniform float u_ScreenRatio;
uniform vec3 u_LightPosition;
uniform vec3 u_CameraPosition;

//
const vec3 ray00 = vec3(-1*u_ScreenRatio,-1, -1.2);
const vec3 ray01 = vec3(-1*u_ScreenRatio,+1, -1.2);
const vec3 ray10 = vec3(+1*u_ScreenRatio,-1, -1.2);
const vec3 ray11 = vec3(+1*u_ScreenRatio,+1, -1.2);
const ivec2 size = imageSize(IMG);

const mat3 mat_rotate = mat3(-0.5, -0.86602540378443864676372317075294, 0, 0.86602540378443864676372317075294, -0.5, 0, 0, 0, 1);
struct plane {
vec3 v_plane;
vec3 n_plane;
vec3 p_plane;
};

//
plane m[3];
int last_plane;

//----------------------------------------------------------
float ray_intersect_sphere(vec3 orig, vec3 dir, vec4 Shape_obj) {
vec3 l = Shape_obj.xyz - orig;
float tca = dot(l,dir);
float d2 = dot(l,l) - tca * tca;
if (d2 > Shape_obj.w * Shape_obj.w) {return 0;}
float thc = sqrt(Shape_obj.w * Shape_obj.w - d2);
float t0 = tca - thc;
float t1 = tca + thc;
if (t0 < 0) {t0 = t1;}
if (t0 < 0) {return 0;}
return t0;
}
//---------------------------------------------------------
'float ray_intersect_plane(in vec3 orig, in vec3 dir, inout plane p) {
vec3 tested_direction = p.v_plane - orig;
float k = dot(tested_direction, p.v_plane) / dot(dir, p.v_plane);
if (k>=0) {
vec3 p0 = orig + dir * k;
// z
if ((p0.z>-80)&&(p0.z<3)) {
p.p_plane = p0;
return length(p0-orig);
}
}
return 1000000;
}'+
//---------------------------------------------------------
bool all_obj(inout vec3 loc_eye, inout vec3 dir, inout vec3 c) {
float min_len = 1000000;
uint near_id = 0;
float len;
float min_len2 = 1000000;
int near_id2 = -1;
for (int i=0; i<3; i++) {
if (i!=last_plane) {
len = ray_intersect_plane(loc_eye, dir, m[i]);
if (len<min_len2) {
min_len2 = len;
near_id2 = i;
}
}
}

//
if (near_id2>=0) {
loc_eye = m[near_id2].p_plane;
dir = reflect(dir, m[near_id2].n_plane);
last_plane =near_id2;
return true;
}

for (uint i=0; i<Shape_obj.length(); i++) {
len = ray_intersect_sphere(loc_eye, dir, Shape_obj[i]);
if ((len>0)&&(len<min_len)) {
min_len = len;
near_id = i;
}
}
//
if (min_len>=1000000) {return false;}

vec3 hit = loc_eye + dir * min_len;
vec3 Normal = normalize(hit - Shape_obj[near_id].xyz);
vec3 to_light = u_LightPosition - hit;
float to_light_len = length(to_light);
vec3 light_dir = normalize(to_light);
float diffuse_light = max(dot(light_dir, Normal), 0.0);
c = min(c + Sphere_color[near_id].xyz * (diffuse_light*0.8+0.2),1);
return false;
}
//---------------------------------------------------------
void main(void) {
if (gl_GlobalInvocationID.x >= size.x || gl_GlobalInvocationID.y >= size.y) return;
const vec2 pos = gl_GlobalInvocationID.xy * u_InvScreenSize.xy;
vec3 dir = normalize(mix(mix(ray00, ray01, pos.y), mix(ray10, ray11, pos.y), pos.x));
vec3 c = vec3(0, 0, 0);
//
vec3 eye = vec3(u_CameraPosition);

//
m[0].v_plane = vec3(0,-5,0);
m[0].n_plane = vec3(0,1,0);
m[1].v_plane = mat_rotate * m[0].v_plane;
m[1].n_plane = mat_rotate * m[0].n_plane;
m[2].v_plane = mat_rotate * m[1].v_plane;
m[2].n_plane = mat_rotate * m[1].n_plane;

//
for (int i=0; i<20; i++) {
if (!all_obj(eye, dir, c)) {break;}
}

//
imageStore(IMG, ivec2(gl_GlobalInvocationID.xy), vec4(c,1));
}


Dalam pendekatan lain, saya menggunakan properti periodisitas dari pola kaleidoskop. Setiap simpul selalu terhubung dengan dua lainnya, di sini tiga simpul ditunjukkan oleh tiga warna.
Kami mengisi objek buffer dengan koordinat simpul segitiga sama sisi yang membentuk belah ketupat.



Dalam gambar, warna diganti dengan angka. Harap dicatat: baris genap dan ganjil diulangi dengan pergeseran satu. Kami memotong elemen yang tidak perlu, hanya menampilkan indeks titik yang diperlukan dan sebagai hasilnya kami mendapatkan segi enam yang dapat dengan mudah diskalakan.



Selanjutnya, ganti warna dengan koordinat tekstur dari template mini-tekstur.



Contoh mengisi tekstur dengan persegi panjang warna acak.

Untuk meningkatkan tampilan, tingkatkan segi enam dengan ukuran layar, dan juga tambahkan rotasi aksial.

Setelah beberapa menit kontemplasi dari rotasi ke satu arah, ia mulai bergerak. Untuk menghilangkan efek yang tidak menyenangkan ini, rotasi diterapkan secara berurutan di setiap arah.

Awalnya, teksturnya dipenuhi dengan elemen acak, tetapi kemudian muncul ide untuk menggunakan gambar warna atau foto. Elemen tampilan melewati gambar dalam arah acak dalam bentuk jendela geser, secara berkala mengubah arah. Jadi polanya lebih jenuh dan menarik.

Hasilnya adalah gambar yang cukup bagus







Video
(Saya tidak tahu cara membuat video, saya minta maaf atas kualitasnya)










Kode shader sangat sederhana.

Kode Shader GLSL
 //  #version 330 core layout (location = 0) in vec4 a_Position; uniform mat4 u_MVP; out vec4 v_Color; out vec2 v_TexCoords; void main() { v_TexCoords = a_Position.zw; gl_Position = u_MVP * vec4(a_Position.xy, 0, 1); } //  #version 330 core precision mediump float; varying vec2 v_TexCoords; uniform sampler2D u_Texture; void main(){ gl_FragColor = texture(u_Texture, v_TexCoords); } 


Anak-anak merasa puas, dan saya melakukan meditasi selama beberapa malam.

Demo (EXE untuk Windows)

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


All Articles