Текстурированная краска для стен


48

Краска на стенах в моей комнате имеет случайную, почти фрактальную, трехмерную текстуру:

Изображение А

В этом задании вы напишите программу, которая генерирует случайные изображения, которые выглядят так, как будто они могут быть частью моих стен.

Ниже я собрал 10 изображений различных пятен на моих стенах. Все они имеют примерно одинаковое освещение, и все они были сняты камерой на расстоянии одного фута от стены. Границы были равномерно обрезаны, чтобы сделать их 2048 на 2048 пикселей, затем они были масштабированы до 512 на 512. Изображение выше - это изображение А.

Это только миниатюры, кликните по картинке для просмотра в полном размере!

A: B: C: D: E:Изображение А Изображение Б Изображение C Изображение D Изображение E

F: G: H: I: J:Изображение F Изображение G Изображение H Изображение я Изображение J

Ваша задача - написать программу, которая в качестве случайного начального числа принимает положительное целое число от 1 до 2 16 и для каждого значения генерирует отдельное изображение, которое выглядит так, как будто это было «одиннадцатое изображение» моей стены. Если кто-то, смотрящий на мои 10 и несколько ваших изображений, не может определить, какие из них были сгенерированы компьютером, значит, вы справились очень хорошо!

Пожалуйста, покажите несколько сгенерированных изображений, чтобы зрители могли увидеть их без необходимости запуска кода.

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

подробности

  • Вы можете использовать инструменты обработки изображений и библиотеки.
  • Используйте входные данные любым удобным для вас способом (командная строка, стандартный ввод, очевидная переменная и т. Д.).
  • Выходное изображение может быть в любом обычном формате файла изображения без потерь, или оно может быть просто отображено в окне / Bowser.
  • Вы можете программно анализировать мои 10 изображений, но не предполагайте, что каждый, кто запускает ваш код, имеет к ним доступ.
  • Вы должны генерировать изображения программно. Вы не можете жестко запрограммировать небольшой вариант одного из моих изображений или другого изображения. (Люди все равно проголосуют за тебя.)
  • Вы можете использовать встроенные генераторы псевдослучайных чисел и предполагать, что период равен 2 16 или более.

счет

Это конкурс популярности, поэтому победитель получает голос с наибольшим количеством голосов.


PerlinNoise + усечение + затенение
осьминог

21
Я не могу делать настенные изображения, поэтому вместо этого сделайте комикс !
Sp3000

8
@ Sp3000 Примерно так и случилось. Хотя, если бы я смотрел вверх, я бы, наверное, выбрал свой потолок , который также мог бы работать ...
Увлечения Кэлвина

Ответы:


65

GLSL (+ JavaScript + WebGL)

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

Живая демо | GitHub репозиторий

Как пользоваться

Перезагрузите страницу для нового случайного изображения. Если вы хотите кормить определенное начальное число, откройте консоль браузера и позвоните drawScreen(seed). Консоль должна отображать семена, используемые при загрузке.

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

Новое: теперь вы можете немного оживить стены, установив флажок «Подвижный источник света».

Что это за колдовство?

У меня есть этот шаблонный код WebGL, плавающий вокруг моей учетной записи GitHub , который я использую время от времени для быстрого прототипирования некоторых 2D-объектов в 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чтобы выровнять верх.
  • Добавьте еще две октавы для немного больше текстуры на верхнем слое.
  • Вычислить нормали полученной поверхности.
  • Выполните простое затенение Фонг по этой поверхности, с зеркальным и рассеянным светом. Цвета выбираются на основе случайных цветов, которые я выбрал из первого примера изображения.

17
Это более реалистично, чем сама стена: o
Квентин

1
Еще несколько "вен" / "змей" / "червей" сделают эту картину более подходящей для "стены". Но все равно приятно.
Нова

33

Mathematica Spackling

Приложение ниже применяет крапинку к случайному изображению. Нажатие на «новый патч» создает новое случайное изображение для работы, а затем применяет эффекты в соответствии с текущими настройками. Эффекты - масляная живопись, фильтр Гаусса, постеризация и тиснение. Каждый эффект может быть независимо настроен. Начальное число для генератора случайных чисел может быть любым целым числом от 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}]

масло


Эффект фильтра Гаусса применяется к Лене (не к версии с эффектом масляной живописи Лены). Радиус 10 пикселей. (В окончательной версии в верхней части этой записи GaussianFilter применяется как конечный эффект.)

 GaussianFilter[Lena, 10]

Лена Гауссиан.


Несколько более мягкий эффект фильтра Гаусса применяется к r1. Радиус 5 пикселей.

 r2 = GaussianFilter[r1, 5]

гаусс


Интенсивный эффект постеризации применительно к Лене. (В окончательной версии приложения я удалил постеризацию. Но мы оставим это в анализе, поскольку примеры в анализе были основаны на более ранней версии с постеризацией.)

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

лена постеризовать


Эффект постеризации, примененный к r2.

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

Постеризовать


Тиснение Лены

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

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


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

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

выбивать


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

lena4


... есть встроенный, чтобы получить изображение Лены? ЛОЛ.
user253751

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сделал меня лол. Странно то, что на последнем изображении Лены видны ацтеки или инки, обращенные влево, в головном уборе и с веточкой, как будто это пистолет.
Уровень Река St

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.