Текстурована на стіні текстурована фарба для стін


48

Фарба на стінах моєї кімнати має випадкову, майже фрактальну, тривимірну текстуру:

Зображення A

У цьому виклику ви напишете програму, яка генерує випадкові зображення, схожі на те, що вони можуть бути частиною моїх стін.

Нижче я зібрав 10 зображень різних плям на своїх стінах. Усі мають приблизно однакове освітлення, і всі були зняті камерою однією ногою від стіни. Межі були рівномірно обрізані, щоб їх було 2048 на 2048 пікселів, а потім їх масштабували до 512 на 512. Зображення вище зображення A.

Це лише ескізи, клацніть зображення для перегляду в повному розмірі!

A: B: C: D: E:Зображення A Зображення B Зображення C Зображення D Зображення E

F: G: H: I: J:Зображення F Зображення G Зображення H Зображення I Зображення J

Ваше завдання полягає в тому, щоб написати програму, яка приймає додатне ціле число від 1 до 2 16 як випадкове насіння, і для кожного значення створюється чітке зображення, схоже, що це могло бути "одинадцятим зображенням" моєї стіни. Якщо хтось, дивлячись на мої 10 зображень і кілька ваших, не може сказати, які були створені на комп’ютері, то ви зробили дуже добре!

Будь ласка, покажіть кілька створених зображень, щоб глядачі могли їх бачити, не запускаючи код.

Я усвідомлюю, що освітлення в моїх зображеннях не ідеально рівномірне за інтенсивністю чи кольором. Вибачте за це, але це найкраще, що я міг зробити без кращого освітлювального обладнання. Ваші зображення не повинні мати змінне освітлення (хоча вони могли). Текстура - це більш важлива річ, на яку слід зосередити увагу.

Деталі

  • Ви можете використовувати засоби обробки зображень та бібліотеки.
  • Візьміть дані будь-яким загальним способом (командний рядок, stdin, очевидна змінна тощо).
  • Вихідне зображення може бути у будь-якому загальному форматі файлу зображень без втрат, або просто відображатися у вікні / винищувачі.
  • Ви можете програмно проаналізувати мої 10 зображень, але не вважайте, що кожен, хто працює з вашим кодом, має доступ до них.
  • Зображення потрібно генерувати програмно. Ви можете не жорстко зафіксувати невеликий варіант одного з моїх зображень або якогось іншого запасного зображення. (Люди все одно би вас проголосували за це.)
  • Ви можете використовувати вбудовані генератори псевдовипадкових чисел і припускати, що період становить 2 16 і більше.

Оцінка балів

Це конкурс на популярність, тому виграє відповідь з найвищим голосом.


PerlinNoise + усікання + затінення
восьминога

21
Я не можу створювати настінні зображення, тому натомість маю комічний !
Sp3000

8
@ Sp3000 Так чи інакше це сталося. Хоча якби я шукав, я, мабуть, вибрав би свою стелю , яка могла б так само працювати ...
Хобі Кальвіна

Відповіді:


65

GLSL (+ JavaScript + WebGL)

введіть тут опис зображення

Жива демонстрація | Сховище GitHub

Як користуватись

Перезавантажте сторінку для нового випадкового зображення. Якщо ви хочете годувати певним зерном, відкрийте консоль браузера та зателефонуйте drawScreen(seed). На консолі повинно відображатися насіння, яке використовується при навантаженні.

Я не дуже перевіряв це на багатьох платформах, тому дайте мені знати, якщо це не працює для вас. Звичайно, ваш браузер повинен підтримувати WebGL. Помилки відображаються або в стовпці зліва, або в консолі браузера (залежно від типу помилки).

Нове: Тепер ви можете трохи оживити стіни, поставивши галочку "Рухоме джерело світла".

Що це за чаклунство?

У мене цей код котла WebGL плаває навколо мого облікового запису GitHub , який я раз у раз використовую для швидкого прототипування деяких двовимірних графічних речей у WebGL. Маючи шейдерну магію, ми також можемо зробити її трохи поміркованою, тому я подумав, що це найшвидший спосіб отримати деякі приємні ефекти. Більшість налаштувань відбувається з цього кодового коду, і я вважаю, що бібліотека для цього подання не буде включати її в цю посаду. Якщо вам цікаво, подивіться на main.js на GitHub (та інші файли у цій папці).

Все, що потрібно робити в JavaScript, - це встановити контекст WebGL, зберігати насіння в однострої для шейдера, а потім зробити один квадратик у всьому контексті. Вершина шейдера - це простий прохідний шейдер, тому вся магія відбувається в фрагменті шейдера. Ось чому я назвав це поданням GLSL.

Найбільша частина коду насправді генерує шум Simplex, який я знайшов у GitHub . Тому я пропускаю це також у наведеному нижче коді. Важлива частина полягає в тому, що вона визначає функцію, snoise(vec2 coords)яка повертає симплексний шум без використання текстури або пошуку масиву. Це зовсім не засіяно, тому хитрість отримання різного шуму полягає у використанні насіння для визначення місця пошуку.

Отже, ось що:

#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;
}

Це воно. Я можу додати ще кілька пояснень завтра, але основна ідея:

  • Виберіть випадкове положення світла.
  • Додайте кілька октав шуму для створення фрактальної картини.
  • Помістіть шум, щоб дно було шорстким.
  • Подайте шум tanhдо рівня, що знаходиться вгорі.
  • Додайте ще дві октави для трохи більше текстури на верхньому шарі.
  • Обчисліть норми отриманої поверхні.
  • Виконайте просту затінку Phong над цією поверхнею, блискучими та розсіяними фарами. Кольори вибираються на основі деяких випадкових кольорів, які я вибрав із першого прикладу зображення.

17
Це більш реально, ніж сама стіна: o
Квентін

1
Ще кілька «жилок» / «змій» / «черв’яків» зробить цю картину більш придатною для «стіни». Але все одно приємно.
Нова

33

Математика шпаклювання

Додаток нижче застосовує креслення до випадкового зображення. Клацання на "новий патч" генерує нове випадкове зображення для роботи, а потім застосовує ефекти відповідно до поточних налаштувань. Ефекти - масляна фарба, гауссовий фільтр, постеризація та тиснення. Кожен ефект можна підлаштувати самостійно. Насінням для генератора випадкових чисел може бути будь-яке ціле число від 1 до 2 ^ 16.

Оновлення : фільтр Гаусса, який пом’якшує краї, тепер останній застосований ефект зображення. З цією модифікацією ефект постеризації більше не потрібен, і таким чином було усунено.

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}])]

кінцевий результат


Пояснення

Пояснення ґрунтується на дещо іншій версії, в якій постеризація використовувалася і GaussianFilterзастосовувалася на початку. Але це все ще служить для з'ясування того, як кожен ефект зображення змінює зображення. Кінцевим результатом є текстура фарби з більш гострими краями. Якщо застосовано фільтр Гаусса лише в кінці, результат буде більш плавним, як показано на малюнку вище.

Давайте розглянемо деякі ефекти зображення, по одному.

Створення вихідного зображення.

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

випадкове зображення


Лена покаже нам, як кожен ефект зображення перетворює життєву картину.

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

Лена


Ефект малювання маслом, нанесений на Лену.

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

олія лена

Ефект картини маслом застосовано до нашого випадкового зображення. Ефект посилився (16 замість 8).

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

олія


Ефект фільтра Гаусса застосований до Lena (не для версії ефекту олійного живопису Lena). Радіус - 10 пікселів. (У остаточній версії, вгорі цього запису, як кінцевий ефект застосовується GaussianFilter.)

 GaussianFilter[Lena, 10]

lena gaussian.


Дещо м'якший ефект фільтра Гаусса, застосований до r1. Радіус - 5 пікселів.

 r2 = GaussianFilter[r1, 5]

гаус


Інтенсивний ефект постерізації застосований до Lena. (В остаточній версії програми я видалив постеризацію. Але ми залишимо це в аналізі, оскільки приклади в аналізі були засновані на більш ранній версії з постеризацією.)

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

lena posterize


Ефект постеризації, застосований до r2.

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

плакати


Тиснення Лени

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

лена тиснення


Тиснення r3 завершує обробку зображення. Це покликане виглядати щось на зразок стелі ОП.

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

тиснення


Для допитливих ось Лена з тими ж застосованими ефектами зображення.

lena4


... є вбудований, щоб отримати зображення Лени? ЛОЛ.
користувач253751

7
Так. У Mathematica є приблизно 30 вбудованих картинок, які використовуються для тестів на обробку зображень.
DavidC

З невеликою модифікацією можна годувати RandomIntegerнасіння, гарантуючи тим самим певний вихід. Або ви маєте на увазі щось інше, наприклад, вихідне, не випадкове зображення, до якого застосовуються ефекти?
DavidC

1
Тепер він приймає насіння від 1 до 2 ^ 16.
DavidC

1
+1, тому що Lena will show us how each image effect transforms a life-like pictureзробив мене ЛОЛ. Дивна річ у тому, що на останньому зображенні Лени, здається, ацтек або інка звернені ліворуч, вдягають головний убір і обмацують гілочку, ніби це пістолет.
Рівень річки Св.

13

POV-Ray

Багато гольф-потенціалу, бігайте з povray /RENDER wall.pov -h512 -w512 -K234543 введіть тут опис зображення

Спочатку він створює випадкову текстуру, але замість того, щоб зупинятися на ній, вона перетворює текстуру в поле 3D-висоти, щоб зробити радіальні тіні від спалаху камери більш реалістичними. І на користь це додає ще одну текстуру невеликих задирок зверху.
Єдиний спосіб, крім жорсткого кодування випадкового насіння, - використовувати clockзмінну, призначену для анімації, це передається з -K{number}прапором

#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>
}

Тут взагалі немає необхідності в гольфі. Приємні результати! :)
Мартін Ендер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.