Komputer Menghasilkan Cat Dinding Bertekstur

48

Cat di dinding di kamar saya memiliki tekstur 3 dimensi acak, hampir seperti fraktal:

Gambar A

Dalam tantangan ini, Anda akan menulis sebuah program yang menghasilkan gambar acak yang terlihat seperti bagian dari dinding saya.

Di bawah ini saya telah mengumpulkan 10 gambar tempat yang berbeda di dinding saya. Semua memiliki pencahayaan yang kira-kira sama dan semuanya diambil dengan kamera satu kaki dari dinding. Perbatasan dipotong secara merata untuk menjadikannya 2048 kali 2048 piksel, kemudian ditingkatkan menjadi 512 kali 512. Gambar di atas adalah gambar A.

Ini hanya thumbnail, klik gambar untuk melihat ukuran penuh!

A: B: C: D: E:Gambar A Gambar B Gambar C Gambar D Gambar E

F: G: H: I: J:Gambar F Gambar G Gambar H Gambar I Gambar J

Tugas Anda adalah menulis sebuah program yang menggunakan bilangan bulat positif dari 1 hingga 2 16 sebagai seed acak, dan untuk setiap nilai menghasilkan gambar yang berbeda yang kelihatannya bisa menjadi "gambar kesebelas" dari dinding saya. Jika seseorang melihat 10 gambar saya dan beberapa dari Anda tidak dapat membedakan mana yang dihasilkan komputer maka Anda telah melakukannya dengan sangat baik!

Harap pamerkan beberapa gambar yang Anda buat sehingga pemirsa dapat melihatnya tanpa harus menjalankan kode.

Saya menyadari bahwa pencahayaan dalam gambar saya tidak seragam sempurna dalam intensitas atau warna. Saya minta maaf untuk ini tetapi itu yang terbaik yang bisa saya lakukan tanpa peralatan pencahayaan yang lebih baik. Gambar Anda tidak perlu memiliki pencahayaan variabel (meskipun mereka bisa). Tekstur adalah hal yang lebih penting untuk difokuskan.

Detail

  • Anda dapat menggunakan alat dan perpustakaan pengolah gambar.
  • Ambil input dengan cara umum yang Anda inginkan (baris perintah, stdin, variabel yang jelas, dll).
  • Gambar output dapat dalam format file gambar lossless umum, atau hanya dapat ditampilkan di jendela / bowser.
  • Anda dapat menganalisis 10 gambar saya secara terprogram tetapi jangan berasumsi bahwa semua orang yang menjalankan kode Anda memiliki akses ke gambar itu.
  • Anda harus menghasilkan gambar secara terprogram. Anda tidak boleh membuat hard-code varian sedikit dari salah satu gambar saya atau gambar stok lainnya. (Lagi pula, orang akan memilih Anda untuk ini.)
  • Anda dapat menggunakan generator nomor pseudorandom bawaan dan menganggap periode adalah 16 16 atau lebih.

Mencetak gol

Ini adalah kontes popularitas sehingga jawaban dengan suara terbanyak menang.

Hobi Calvin
sumber
PerlinNoise + pemotongan + naungan
Octopus
21
Saya tidak bisa membuat gambar dinding, jadi alih-alih punya komik !
Sp3000
8
@ Sp3000 Begitulah yang terjadi. Meskipun jika saya telah melihat ke atas, saya mungkin akan memilih langit-langit saya , yang dapat bekerja juga ...
Calvin's Hobbies

Jawaban:

65

GLSL (+ JavaScript + WebGL)

masukkan deskripsi gambar di sini

Demo langsung | Repositori GitHub

Cara Penggunaan

Muat ulang halaman untuk gambar acak baru. Jika Anda ingin memberi makan di seed tertentu, buka konsol browser Anda dan panggil drawScreen(seed). Konsol harus menampilkan benih yang digunakan saat memuat.

Saya belum benar-benar menguji ini pada banyak platform, jadi beri tahu saya jika itu tidak berhasil untuk Anda. Tentu saja, browser Anda perlu mendukung WebGL. Kesalahan ditampilkan baik di kolom di sebelah kiri, atau di konsol browser (tergantung pada jenis kesalahan).

Baru: Sekarang Anda dapat menghidupkan sedikit dinding, dengan mencentang kotak "sumber cahaya bergerak".

Apa sihir ini?

Saya punya kode boilerplate WebGL ini mengambang di sekitar akun GitHub saya , yang saya gunakan setiap sekarang dan kemudian dengan cepat membuat prototipe beberapa hal grafis 2D di WebGL. Dengan beberapa shader magic, kita juga bisa membuatnya terlihat sedikit 3D, jadi saya pikir itu cara tercepat untuk mendapatkan beberapa efek yang bagus. Sebagian besar penyiapan berasal dari kode boilerplate itu, dan saya sedang mempertimbangkan pustaka untuk pengiriman ini dan tidak akan memasukkannya dalam posting ini. Jika Anda tertarik, lihat main.js di GitHub (dan file lain di folder itu).

Semua yang dilakukan JavaScript adalah mengatur konteks WebGL, menyimpan seed dalam seragam untuk shader, dan kemudian membuat quad tunggal di seluruh konteks. Vertex shader adalah shader passthrough sederhana, sehingga semua keajaiban terjadi dalam fragmen shader. Itu sebabnya saya menyebut ini pengiriman GLSL.

Bagian terbesar dari kode sebenarnya adalah untuk menghasilkan noise Simplex, yang saya temukan di GitHub . Jadi saya menghilangkan itu juga dalam daftar kode di bawah ini. Bagian yang penting adalah, ia mendefinisikan fungsi snoise(vec2 coords)yang mengembalikan noise simplex tanpa menggunakan pencarian tekstur atau array. Ini tidak diunggulkan sama sekali, jadi trik untuk mendapatkan suara yang berbeda adalah dengan menggunakan benih dalam menentukan di mana harus melakukan pencarian.

Jadi begini:

#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable

uniform float uSeed;
uniform vec2 uLightPos;

varying vec4 vColor;
varying vec4 vPos;

/* ... functions to define snoise(vec2 v) ... */

float tanh(float x)
{
    return (exp(x)-exp(-x))/(exp(x)+exp(-x));
}

void main() {
    float seed = uSeed * 1.61803398875;
    // Light position based on seed passed in from JavaScript.
    vec3 light = vec3(uLightPos, 2.5);
    float x = vPos.x;
    float y = vPos.y;

    // Add a handful of octaves of simplex noise
    float noise = 0.0;
    for ( int i=4; i>0; i-- )
    {
        float oct = pow(2.0,float(i));
        noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
    }
    // Level off the noise with tanh
    noise = tanh(noise*noise)*2.0;
    // Add two smaller octaves to the top for extra graininess
    noise += sqrt(abs(noise))*snoise(vec2(mod(seed,13.0)+x*32.0,mod(seed*seed,11.0)+y*32.0))/32.0*3.0;
    noise += sqrt(abs(noise))*snoise(vec2(mod(seed,13.0)+x*64.0,mod(seed*seed,11.0)+y*64.0))/64.0*3.0;

    // And now, the lighting
    float dhdx = dFdx(noise);
    float dhdy = dFdy(noise);
    vec3 N = normalize(vec3(-dhdx, -dhdy, 1.0)); // surface normal
    vec3 L = normalize(light - vec3(vPos.x, vPos.y, 0.0)); // direction towards light source
    vec3 V = vec3(0.0, 0.0, 1.0); // direction towards viewpoint (straight up)
    float Rs = dot(2.0*N*dot(N,L) - L, V); // reflection coefficient of specular light, this is actually the dot product of V and and the direction of reflected light
    float k = 1.0; // specular exponent

    vec4 specularColor = vec4(0.4*pow(Rs,k));
    vec4 diffuseColor = vec4(0.508/4.0, 0.457/4.0, 0.417/4.0, 1.0)*dot(N,L);
    vec4 ambientColor = vec4(0.414/3.0, 0.379/3.0, 0.344/3.0, 1.0);

    gl_FragColor = specularColor + diffuseColor + ambientColor;
    gl_FragColor.a = 1.0;
}

Itu dia. Saya mungkin menambahkan beberapa penjelasan lagi besok, tetapi ide dasarnya adalah:

  • Pilih posisi cahaya acak.
  • Tambahkan beberapa oktaf suara, untuk menghasilkan pola fraktal.
  • Kuadratkan suara untuk menjaga bagian bawah tetap kasar.
  • Beri makan kebisingan melalui tanhuntuk naik dari atas.
  • Tambahkan dua oktaf lagi untuk sedikit lebih banyak tekstur pada lapisan atas.
  • Hitung normals dari permukaan yang dihasilkan.
  • Jalankan bayangan Phong sederhana di atas permukaan itu, dengan lampu specular dan difus. Warna dipilih berdasarkan beberapa warna acak yang saya ambil dari contoh gambar pertama.
Martin Ender
sumber
17
Ini lebih realistis daripada tembok itu sendiri: o
Quentin
1
Beberapa "vena" / "ular" / "cacing" lainnya akan membuat gambar ini lebih cocok untuk "dinding". Tapi tetap menyenangkan.
Nova
33

Mathematica Spackling

Aplikasi di bawah ini berlaku speckling ke gambar acak. Mengklik "tambalan baru" menghasilkan gambar acak baru untuk bekerja, dan kemudian menerapkan efek sesuai dengan pengaturan saat ini. Efeknya adalah lukisan minyak, filter Gaussian, posterisasi, dan embossing. Setiap efek dapat di-tweak secara independen. Benih untuk pembangkit bilangan acak dapat berupa bilangan bulat dari 1 hingga 2 ^ 16.

Pembaruan : Filter Gaussian, yang melembutkan tepi, sekarang merupakan efek gambar terakhir yang diterapkan. Dengan modifikasi ini, efek posterisasi tidak lagi diperlukan dan karenanya dihilangkan.

Manipulate[
 GaussianFilter[ImageEffect[ImageEffect[r, {"OilPainting", o}], {"Embossing", e, 1.8}], g],
 Button["new patch", (SeedRandom[seed] r = RandomImage[1, {400, 400}])], 
 {{o, 15, "oil painting"}, 1, 20, 1, ContinuousAction -> False, Appearance -> "Labeled"}, 
 {{e, 1.64, "embossing"}, 0, 5, ContinuousAction -> False, Appearance -> "Labeled"},
 {{g, 5, "Gaussian filter"}, 1, 12, 1, ContinuousAction -> False, Appearance -> "Labeled"},
 {{seed, 1}, 1, 2^16, 1, ContinuousAction -> False, Appearance -> "Labeled"}, 
 Initialization :> (SeedRandom[seed]; r = RandomImage[1, {400, 400}])]

hasil akhir


Penjelasan

Penjelasannya didasarkan pada versi yang sedikit berbeda, di mana posterisasi digunakan dan GaussianFilterditerapkan sejak awal. Tetapi masih berfungsi untuk memperjelas bagaimana setiap efek gambar mengubah gambar. Hasil akhirnya adalah tekstur cat dengan tepi yang lebih tajam. Ketika filter Gaussian hanya diterapkan di akhir, hasilnya akan lebih halus, seperti yang ditunjukkan gambar di atas.

Mari kita lihat beberapa efek gambar, satu per satu.

Buat gambar awal.

 r = RandomImage[1, {200, 200}]

gambar acak


Lena akan menunjukkan kepada kita bagaimana setiap efek gambar mengubah gambar yang hidup.

Lena = ExampleData[{"TestImage", "Lena"}]

Lena


Efek lukisan cat minyak diterapkan pada Lena.

ImageEffect[Lena, {"OilPainting", 8}]

minyak lena

Efek lukisan cat minyak diterapkan pada gambar acak kami. Efeknya diintensifkan (16 bukannya 8).

 r1 = ImageEffect[r, {"OilPainting", 16}]

minyak


Efek filter Gaussian diterapkan pada Lena (bukan ke versi efek lukisan cat minyak Lena). Radiusnya 10 piksel. (Dalam versi final, di bagian atas entri ini, GaussianFilter diterapkan sebagai efek akhir.)

 GaussianFilter[Lena, 10]

lena gaussian.


Efek filter Gaussian yang agak lebih ringan diterapkan ke r1. Radiusnya adalah 5 piksel.

 r2 = GaussianFilter[r1, 5]

gauss


Efek posterisasi intens diterapkan pada Lena. (Dalam versi final aplikasi, saya menghapus posterisasi. Tetapi kita akan membiarkannya dalam analisis, karena contoh-contoh dalam analisis didasarkan pada versi sebelumnya dengan posterisasi.)

 ImageEffect[Lena, {"Posterization", 2}]

lena posterize


Efek posterisasi diterapkan pada r2.

r3 = ImageEffect[r2, {"Posterization", 4}]

posterize


Lena timbul

 ImageEffect[Lena, {"Embossing", 1.2, 1.8}]

lena timbul


Embossing r3 melengkapi pemrosesan gambar. Ini dimaksudkan agar terlihat seperti langit-langit OP.

 ceilingSample = ImageEffect[r3, {"Embossing", 1.2, 1.8}]

menatah


Bagi yang penasaran, inilah Lena dengan efek gambar yang sama diterapkan.

lena4

DavidC
sumber
... ada built-in untuk mendapatkan gambar Lena? LOL.
user253751
7
Iya. Ada sekitar 30 gambar bawaan di Mathematica yang digunakan untuk tes pemrosesan gambar.
DavidC
Dengan sedikit modifikasi, seseorang dapat memberi makan RandomIntegerbenih, sehingga menjamin output tertentu. Atau maksud Anda sesuatu yang lain, seperti gambar awal, non-acak yang efeknya diterapkan?
DavidC
1
Sekarang menerima benih dari 1 hingga 2 ^ 16.
DavidC
1
+1 karena Lena will show us how each image effect transforms a life-like picturemembuat saya LOL. Yang aneh adalah, gambar Lena terakhir tampaknya memiliki Aztec atau Inca menghadap ke kiri, mengenakan hiasan kepala dan membuat ranting seolah-olah itu adalah pistol.
Level River St
13

POV-Ray

Banyak potensi bermain golf, jalankan bersama povray /RENDER wall.pov -h512 -w512 -K234543 masukkan deskripsi gambar di sini

Pertama itu menciptakan tekstur acak, tetapi bukannya berhenti di sana itu mengubah tekstur menjadi bidang ketinggian 3D untuk membuat bayangan radial dari flash kamera lebih realistis. Dan untuk ukuran yang baik itu menambahkan tekstur gundukan kecil di atasnya.
Satu-satunya cara selain dari hardcoding benih acak adalah dengan menggunakan clockvariabel yang dimaksudkan untuk animasi, ini dilewatkan dengan -K{number}bendera

#default{ finish{ ambient 0.1 diffuse 0.9 }} 

camera {location y look_at 0 right x}
light_source {5*y color 1}

#declare R1 = seed (clock); // <= change this

#declare HF_Function  =
 function{
   pigment{
     crackle turbulence 0.6
     color_map{
       [0.00, color 0.01]
       [0.10, color 0.05]
       [0.30, color 0.20]
       [0.50, color 0.31]
       [0.70, color 0.28]
       [1.00, color 0.26]
     }// end color_map
    scale <0.25,0.005,0.25>*0.7 
    translate <500*rand(R1),0,500*rand(R1)>
   } // end pigment
 } // end function

height_field{
  function  512, 512
  { HF_Function(x,0,y).gray * .04 }
  smooth 
  texture { pigment{ color rgb<0.6,0.55,0.5>}
            normal { bumps 0.1 scale 0.005}
            finish { phong .1 phong_size 400}
          } // end of texture  
  translate< -0.5,0.0,-0.5>
}
DenDenDo
sumber
Tidak perlu bermain golf ini sama sekali. Hasil yang bagus! :)
Martin Ender