برنامه نویسی

Jergen Normal Mapped – انجمن DEV می شود

مقدمه

در آخرین پستم جنبه هنری را بررسی کردم – که هنوز با کلمه هنر دست و پنجه نرم می کنم – شخصیت بازیکنم. این بار به سراغ چیزهای فنی تر می رویم، اگرچه هنوز کمی “هنرمندانه” است. در حال حاضر جرگن کمی ملایم به نظر می رسد.

بی مزه لی جرگن

بنابراین تصمیم گرفتم که وقت آن رسیده است که چراغ ها را روشن کنم.

بگذار نور وجود داشته باشد

خوب، جرگن در حال حاضر به اندازه کافی روشن به نظر می رسد. این به این دلیل است که سایه زن های فعلی از رنگ بافت به عنوان مقدار رنگ نهایی برای ترسیم استفاده می کنند. بیایید این را تغییر دهیم تا بعداً بتوانیم تأثیر نورهایی که بر روی جرگن تابیده می شود را ببینیم.

ابتدا مقداری نور محیط اضافه می کنیم. من در تلاش برای توضیح نور محیط به زبان خودم هستم، اما برای من این فقط سطح نور پایه است. تمام نوری که از چندین منبع نوری به اطراف پراکنده می شود، بنابراین دیگر نمی توانید واقعاً تشخیص دهید که نور از کجا می آید. بنابراین، فاصله یا جهت منبع نور اهمیتی ندارد. من احساس می‌کنم که سایه‌زن قطعه در توضیح آن بهتر کار می‌کند.

// Fragment shader
#version 330 core

in vec2 TexCoordinate;
out vec4 FragColor;
uniform sampler2D tex;

void main() {
    // ambient light, kind of a base light amount that is just there.
    // All zeros would mean it's completely dark.
    vec4 ambient = vec4(1.0f, 1.0f, 1.0f, 1.0f) * 0.2f;
    FragColor = texture(tex, TexCoordinate) * ambient;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نور محیط سفید است، اما تا 1/5 کم شده است.

در حین خواندن این پست، متوجه شدم که اشتباه کردم. من هم دارم فینال میشم FragColor شفاف با ضرب ambient توسط 0.2f. این باعث می شود کل تصویر تیره تر شود. تمام تصاویر این پست حاوی این اشتباه است. GIF در انتها این را تصحیح می کند و استفاده می کند vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f); در عوض

برای کامل بودن، در اینجا سایه زن رأس مربوطه آمده است:

// Vertex shader
#version 330 core

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inTexCoordinate;

out vec2 TexCoordinates;

void main() {
  gl_Position = vec4(inPosition, 1.0);
  TexCoordinate = aTexCoordinate;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

آیا هنوز می توانید جرگن را ببینید؟ جایی که او هست کاملا تاریک است.

جرگن را پیدا کن

بعد، نور پراکنده را اضافه می کنیم. نور پراکنده نوری است که از یک منبع نور خاص می آید. بنابراین، بر خلاف نور محیط، جهت نور در نحوه تأثیر آن بر جسمی که روشن می کند، نقش دارد. اگر جسم ما رو به منبع نور باشد، باید روشن‌تر باشد، اگر رو به رو باشد باید در سایه باشد. ما باید به منبع نور جایگاهی در دنیای خود بدهیم. با آن، سپس می توانیم جهت جسم خود را به نور محاسبه کنیم.

از آنجایی که قطعه سایه‌زن موقعیت جهانی قطعه‌ای را که سایه می‌اندازد نمی‌داند، باید آن را از سایه‌زن رأس عبور دهیم.

بیایید یک خروجی جدید به سایه زن راس اضافه کنیم و یک مقدار به آن اختصاص دهیم.

// Vertex shader
// ...
out vec2 TexCoordinate;
out vec3 FragPosition;  // 

void main() {
  gl_Position = vec4(inPosition, 1.0);
  TexCoordinate = inTexCoordinate;
  FragPosition = inPosition;  // 
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس ورودی مربوطه را به قطعه سایه زن اضافه می کنیم. ما همچنین متغیرهایی را برای منبع نور خود و یک سطح معمولی برای جسمی که می خواهیم روشن کنیم تعریف می کنیم. متغیرها در قطعه کد زیر توضیح داده شده اند.

// Fragment shader
// ...
in vec2 TexCoordinate;
in vec3 FragPosition; // 
// ...
void main() {
    // ambient light, kind of a base light amount that is just there
    vec4 ambient = vec4(1.0f, 1.0f, 1.0f, 1.0f) * 0.2f;

    // The color our light emits, Halloween season is around the corner,
    // so let's make it a creepy red.
    vec4 lightColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
    vec3 lightPosition = vec3(10.0f, 1.0f, 0.0f);
    // We normalize the direction vector because we're only interested in the direction.
    vec3 lightDirection = normalize(lightPosition - FragPosition);

    // Next we define a normal for the fragment. The normal defines which way a surface is facing.
    // With this information we can figure out if it's facing towards the light source 
    // and should therefore be illuminated. 
    // Our normal for every fragment points to the right side towards the light source.
    vec3 normal = normalize(1.0f, 0.0f, 0.0f);

    // We use the dot product to figure out how much the surface normal and the light direction align.
    float diffuseStrength = max(dot(normal, lightDirection), 0.0f);
    vec4 diffuse = diffuseStrength * lightColor;

    // We now add the diffuse light component to the already existing ambient component before multiplying.
    FragColor = texture(tex, TexCoordinate) * (ambient + diffuse);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر این را برای گوشه سمت چپ بالای مستطیل محاسبه کنیم (-0.25, 0.5) که Jergen ما را نمایش می دهد.
lightDirection = (10, 1, 0) - (-0.25, 0.5) = (10 - -0.25, 1 - 0.5, 0 - 0) = (10.25, 0.5, 0)
که هست (0.998812, 0.0487226, 0) وقتی نرمال شد

قدرت نور پراکنده در این صورت است:
diffuseStrength = (1, 0, 0) * (0.998812, 0.0487226, 0) = 1 * 0.998812 + 0 * 0.0487226 + 0 * 0 = 0.998812
بنابراین نور منتشر قوی است، که منطقی است زیرا سطح نرمال تقریباً موازی با جهت نور است. بیایید ببینیم این چگونه به نظر می رسد.

جرگن در نور قرمز وحشتناک غسل کرد.
جرگن در نور قرمز وحشتناک غسل کرد

بیایید ببینیم اگر حالت عادی را به سمت بالا تغییر دهیم، چه اتفاقی می‌افتد. به این ترتیب جهت عادی و نور تقریباً عمود بر هم هستند، که باید تصویر بسیار تیره‌تری را به همراه داشته باشد.
اما اجازه دهید قبل از بررسی واقعی، سهم پراکنده را محاسبه کنیم.

lightDirection ثابت می ماند. قدرت انتشار جدید است
(0, 1, 0) * (0.99812, 0.0487226, 0) = 0 * 0.998812 + 1 * 0.0487226 + 0 * 0 = 0.0487226، که با انتظارات ما مطابقت دارد.

و حالا، پرده ها. جرگن تقریباً دوباره رفته است.

جرگن در تاریکی پنهان شده است

اکنون که نورپردازی اولیه را در اختیار داریم، بیایید یک قدم جلوتر برویم و نقشه‌برداری معمولی را معرفی کنیم.

نقشه ای از نرمال ها

تا کنون ما از همان نرمال برای همه فرگمنت ها استفاده کرده ایم و آن را مستقیماً در سایه زن قرار داده ایم. ما می‌توانیم نرمال‌ها را در داده‌های راس خود، همراه با موقعیت‌ها و مختصات بافت تعریف کنیم. به این ترتیب ما قادر خواهیم بود برای هر راس یک نرمال مشخص کنیم. برای Jergen این بدان معنی است که ما می توانیم چهار راس را مشخص کنیم زیرا Jergen فقط یک مستطیل است. اما، ما بیشتر می خواهیم. ما می خواهیم بتوانیم به صورت جرگن، کوله پشتی اش، نرمال های مختلفی بدهیم. راه این کار استفاده از یک نقشه معمولی است.

یک نقشه معمولی فقط یک بافت است که در آن رنگ ها بردارهای عادی را نشان می دهند. می توانید نمونه ای را در مقاله ویکی پدیا در مورد نقشه برداری معمولی بررسی کنید

اگر بخواهیم همان نوری را که در حال حاضر داریم بازتولید کنیم، فقط یک بافت ایجاد می‌کنیم که در آن هر پیکسل حاوی مقدار باشد. (1, 0, 0)، یا (0, 1, 0) به ترتیب. اما همانطور که قبلا ذکر شد، ما نرمال های متفاوتی را برای (تقریبا) هر پیکسل می خواهیم. اما چگونه یک نقشه معمولی ایجاد کنیم؟ یک راه آسان، حداقل برای هنر پیکسلی با پیکسل های کم، انتخاب رنگ ها از یک نقشه معمولی موجود است – مانند نقشه ویکی پدیا. و این دقیقاً همان کاری است که من انجام دادم. ابتدا بافت موجود Jergen را کپی کردم و شروع به انتخاب رنگ ها از نقشه معمولی کردم.

چرا نقشه های معمولی همه چیز به نظر می رسند اما عادی هستند؟

اکنون قسمت سرگرم کننده فرا می رسد، ما از آن نقشه استفاده می کنیم تا به جرگن تغییرات بیشتری بدهیم. از آنجایی که ما از یک بافت مجزا برای نقشه معمولی با ابعاد یکسان بافت اصلی استفاده خواهیم کرد، می‌توانیم از مختصات بافت استفاده مجدد کنیم و سایه‌زن راس را همانطور که هست نگه داریم.

با این حال، قطعه سایه زن به چند تغییر نیاز دارد.

// Fragment shader
// ...
uniform sampler2D tex;
uniform sampler2D normalMap; // 
// ...
void main() {
    // ...

    // We now use the normal map to get the normals for the current fragment
    vec3 normal = normalize(texture(normalMap, TexCoordinate).rgb);

    // ...
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و این تمام چیزی است که او نوشته است.

عادی نقشه برداری Jergen

و در اینجا همان چیزی است که در مورد منبع نوری که به دور جرگن می‌چرخد.

نمایش نور جرگن

و یک امتیاز دیگر (نمیدانم چرا کیفیت ویدیو پس از آپلود اینقدر بد است):
https://www.youtube.com/watch?v=fh3O6irnpEQ

مبارزات

هنگامی که به استفاده از نقشه معمولی در سایه زن تغییر مکان دادم، یک سکسکه کوچک در پایان وجود داشت. بافت هیچ تاثیری نداشت. بنابراین من ابتدا اولین بافت را به فایل نقشه معمولی تغییر دادم تا ببینم آیا می توانم نقشه معمولی را ترسیم کنم – همه چیز خوب است. همه چیز را معکوس کنید، سعی کنید نقشه معمولی را به واحد بافت یک متصل کنید، بافت رنگ را روی واحد دو بکشید، … در پایان فقط فراموش کردم واحد بافت دوم – d'Oh را فعال کنم.

ریاضیات نیز ساده‌تر از آنچه پیش‌بینی می‌کردم بود، حاصل ضرب نقطه‌ای را با ضرب ضربدری اشتباه گرفتم، …

در مجموع، این یکی نسبتا آرام پیش رفت. تقسیم کار به گام‌های گاهی مضحک کوچک، مانند اضافه کردن یک متغیر دیگر به سایه‌زن و آزمایش آن یا تعریف متغیرها در سایه‌زن به جای انتقال آنها از کد میزبان، کمک زیادی کرد.

ممنون که خواندید و به مبارزه ادامه دهید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا