Renkler

Önceki bölümlerde renkleri basitçe kullandık ve değiştirdik, ama hiçbir zaman hakkıyla tanımlayamadık. Bu kısımda renklerin ne olduğunu tartışacağız ve önümüzdeki Işıklandırma bölümleri için sahne oluşturmaya başlayacağız.

Gerçek dünyada renkler, her bir nesnenin kendi rengine sahip olduğu bilinen herhangi bir renk değerini alabilir. Dijital dünyada (sonsuz) gerçek renkleri, (sınırlı) dijital değerlerle eşleştirmemiz gerekir ve bu nedenle gerçek dünyadaki tüm renkler dijital olarak temsil edilemez. Renkler, genellikle RGBolarak kısaltılmış kırmızı, yeşilve mavibir bileşen kullanılarak dijital olarak temsil edilir. Sadece bu 3 değerin farklı kombinasyonlarını kullanarak, [0,1] aralığında, neredeyse her rengi temsil edebiliriz. Örneğin, bir mercan rengi elde etmek için bir renk vektörünü şöyle tanımlarız:

glm::vec3 coral(1.0f, 0.5f, 0.31f);   

Gerçek hayatta gördüğümüz bir nesnenin rengi gerçekte sahip olduğu renk değil, nesneden yansıyan renktir. Nesne tarafından emilmeyen (reddedilen) renkler, onu algıladığımız renktir. Örnek olarak, güneşin ışığı, birçok farklı rengin birleşik toplamı olan beyaz bir ışık olarak algılanır (aşağıdaki görselde gördüğünüz gibi). Bu beyaz ışığı mavi bir oyuncak üzerinde parlamasını sağlarsak, mavi renk hariç tüm beyaz rengin alt renklerini emerdi. Oyuncak mavi renk kısmını emmediği için yansıtılır. Bu yansıyan ışık gözümüze gelir ve oyuncağın mavi bir renge sahip gibi görünmesini sağlar. Aşağıdaki görüntü, mercan renkli bir oyuncak için bunu göstermektedir, burada çeşitli yoğunluklarda çeşitli renkleri yansıtmaktadır:

Beyaz güneş ışığının tüm görünür renklerin bir koleksiyonu olduğunu ve nesnenin bu renklerin büyük bir bölümünü emdiğini görebilirsiniz. Yalnızca nesnenin rengini temsil eden renkleri yansıtır ve bunların birleşimi algıladığımız şeydir (bu durumda mercan rengi).

Teknik olarak biraz daha karmaşık, ama PBR bölümlerinde bunları anlayacağız.

Bu renk yansıma kuralları doğrudan grafik alanlarına uygulanır. OpenGL' de bir ışık kaynağı tanımladığımızda, bu ışık kaynağına bir renk vermek isteriz. Önceki paragrafta beyaz bir renk örneği vardı, bu yüzden ışık kaynağına da beyaz bir renk vereceğiz. Daha sonra ışık kaynağının rengini bir nesnenin renk değeri ile çarpacağız, ortaya çıkan renk nesnenin yansıyan rengi (ve aynı zamanda algılanan rengi) olacak. Oyuncağımızı tekrar ziyaret edelim (bu kez mercan rengi değeri ile) ve grafik alanında algılanan rengini nasıl hesaplayacağımızı görelim. Sonuçta elde edilen renk vektörünü, ışık ve nesne renk vektörleri arasında bileşen bazında çarpma yaparak elde ederiz:

glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);

Oyuncağın renginin beyaz ışığın büyük bir kısmını emdiğini, ancak kendi renk değerine göre birkaç kırmızı, yeşil ve mavi değeri yansıttığını görebiliriz. Bu, renklerin gerçek hayatta nasıl çalışacağının bir temsilidir. Böylece bir nesnenin rengini, bir ışık kaynağından yansıttığı her renk bileşeninin miktarı olarak tanımlayabiliriz. Peki yeşil ışık kullanırsak ne olur?

glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);

Görebileceğimiz gibi, oyuncağın emecek veya yansıtacak kırmızı ve mavi ışığı yoktur. Oyuncak ayrıca ışığın yeşil değerinin yarısını emer, ancak ışığın yeşil değerinin yarısını da yansıtır. O zaman algıladığımız oyuncak rengi koyu yeşilimsi bir renk olur. Yeşil bir ışık kullanırsak, sadece yeşil renk bileşenlerinin yansıtılabileceğini ve bu şekilde algılanabileceğini görebiliriz; kırmızı ve mavi renkler algılanmaz. Sonuç olarak, mercan nesnesi aniden koyu yeşilimsi bir nesne haline gelir. Koyu yeşil-yeşil ışıkla bir örnek daha deneyelim:

glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

Gördüğünüz gibi, farklı ışık renkleri kullanan nesnelerden ilginç renkler elde edebiliriz. Renklerle yaratıcı olmak çok da zor değil.

Renkleri deneyebileceğimiz bir sahne oluşturmaya başlayalım.

Bir ışıklandırma sahnesi

Önümüzdeki bölümlerde, renkleri geniş ölçüde kullanan gerçek dünya ışıklandırmasını benzetimleyerek ilginç görseller oluşturacağız. Şimdi ışık kaynaklarını kullanacağımızdan, bunları sahnede görsel nesneler olarak görüntülemek ve ışıklandırmayı benzetimlemek için en az bir nesne eklemek istiyoruz.

İhtiyacımız olan ilk şey ışığı açmak için bir nesne ve önceki bölümlerden gelen kepaze konteyner küpünü kullanacağız. Ayrıca, ışık kaynağının 3- boyutlu sahnede nerede olduğunu göstermek için bir ışık nesnesine ihtiyacımız olacak. Sadelik için ışık kaynağını bir küple temsil edeceğiz (köşe nokta verilerine zaten sahip miyiz ?).

Bu nedenle, bir köşe nokta arabellek nesnesini doldurmak, köşe nokta özniteliği işaretçileri ve tüm bu gibi şeylere sizin aşina olmanız gerekir, bu adımlarda size yol göstermeyeceğiz. Bunların neler olduğu konusunda halen bir fikriniz yoksa, önceki bölümleri gözden geçirmenizi ve devam etmeden önce mümkünse alıştırmalar üzerinde çalışmanızı öneririm.

Yani, ihtiyacımız olan ilk şey konteyneri çizmek için bir köşe nokta gölgelendiricisidir. Konteynerin köşe nokta konumları aynı kalır (bu sefer doku koordinatlarına ihtiyacımız olmayacak olsa da), böylece sizin için kod yeni olmamalıdır. Son bölümlerden köşe nokta gölgelendiricisinin basit bir versiyonunu kullanacağız:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
} 

Köşe nokta verilerini ve öznitelik işaretçilerini yeni köşe nokta gölgelendiricisiyle eşleşecek şekilde güncellediğinizden emin olun (isterseniz doku verilerini ve öznitelik işaretçilerini etkin durumda tutabilirsiniz; şu anda kullanmıyoruz).

Ayrıca bir ışık kaynağı küpü oluşturacağımız için, özellikle ışık kaynağı için yeni bir VAO oluşturmak istiyoruz. Işık kaynağını aynı VAO ile oluşturabilir ve sonra model matrisinde birkaç ışık konumu dönüşümü yapabiliriz, ancak yaklaşan bölümlerde konteyner nesnesinin köşe nokta verilerini ve öznitelik işaretçilerini oldukça sık değiştireceğiz ve bu değişikliklerin ışık kaynağı nesnesine yayılmasını istiyoruz (yalnızca ışık küpünün köşe nokta konumlarını önemsiyoruz), bu yüzden yeni bir VAO oluşturacağız:

unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// we only need to bind to the VBO, the container's VBO's data already contains the data.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// set the vertex attributes (only position data for our lamp)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

Kod nispeten anlaşılır olmalıdır. Şimdi hem konteyneri hem de ışık kaynağı küpünü oluşturduğumuza göre, tanımlanması gereken bir şey var ve bu da hem kapsayıcı hem de ışık kaynağı için parça gölgelendirici:

#version 330 core
out vec4 FragColor;
  
uniform vec3 objectColor;
uniform vec3 lightColor;

void main()
{
    FragColor = vec4(lightColor * objectColor, 1.0);
}

Parça gölgelendirici, uniform bir değişkenin hem bir nesne rengini hem de bir ışık rengi kabul eder. Burada ışığın rengini, bu bölümün başında tartıştığımız gibi nesnenin (yansıyan) rengiyle çarpıyoruz. Yine, bu gölgelendiricinin anlaşılması kolay olacaktır. Beyaz bir ışıkla nesnenin rengini son bölümün mercan rengine ayarlayalım:

// don't forget to use the corresponding shader program first (to set the uniform)
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor",  1.0f, 1.0f, 1.0f);

Unutulmaması gereken, bu ışıklandırma gölgelendiricilerini bir sonraki bölümde güncellemeye başladığımızda, ışık kaynağı küpünün de etkileneceği ve bu bizim istediğimiz şey olmadığıdır. Işık kaynağı nesnesinin renginin ışıklandırma hesaplamalarını etkilemesini istemiyoruz, aksine ışık kaynağını diğerlerinden izole tutmak istiyoruz. Işık kaynağının diğer renk değişikliklerinden etkilenmeyen sabit bir parlak renge sahip olmasını istiyoruz (bu, ışık kaynağı küpünün gerçekten ışığın kaynağı gibi görünmesini sağlar).

Bunu başarmak için, ışık kaynağı küpünü çizmek için kullanacağımız ikinci bir gölgelendirici grubu oluşturmamız gerekir, böylece ışıklandırma gölgelendiricilerindeki değişikliklerden sakınmış oluruz. Köşe nokta gölgelendiricisi, ışıklandırma köşe nokta gölgelendiricisi ile aynıdır, böylece kaynak kodunu kolayca kopyalayabilirsiniz. Işık kaynağı küpünün parça gölgelendiricisi, lamba üzerinde sabit bir beyaz renk tanımlayarak küp renginin parlak kalmasını sağlar:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

Sahnelemek istediğimizde, az önce tanımladığımız ışıklandırma gölgelendiricisini kullanarak konteyner nesnesini (veya muhtemelen başka birçok nesneyi) sahnelemek istiyoruz ve ışık kaynağını çizmek istediğimizde ışık kaynağının gölgelendiricilerini kullanıyoruz. Işıklandırma eğitselleri sırasında, yavaş yavaş daha gerçekçi sonuçlar elde etmek için ışıklandırma gölgelendiricilerini aşama aşama güncelleyeceğiz.

Işık kaynağı küpünün temel amacı ışığın nereden geldiğini göstermektir. Genellikle bir ışık kaynağının sahnenin herhangi bir yerindeki konumunu tanımlarız, ancak bu sadece görsel anlamı olmayan bir konumdur. Işık kaynağının gerçekte nerede olduğunu göstermek için ışık kaynağının aynı yerinde bir küp oluşturuyoruz. Sahnenin ışık koşullarından bağımsız olarak, küpün her zaman beyaz kalmasını sağlamak için bu küpü ışık kaynağı küp gölgelendiricisi ile oluşturuyoruz.

Işık kaynağının dünya-uzayı koordinatlarındaki konumunu temsil eden global bir vec3değişkeni bildirelim:

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

Daha sonra ışık kaynağı küpünü ışık kaynağının konumuna öteler ve sahnelemeden önce ölçeklendiririz:

model = glm::mat4(1.0f);
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); 

Işık kaynağı küpü için en son sahneleme kodu aşağıdaki gibi görünmelidir:

lightCubeShader.use();
// set the model, view and projection matrix uniforms
[...]
// draw the light cube object
glBindVertexArray(lightCubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);			

Tüm kod parçalarının uygun konumlarına yerleştirilmesi, ışıklandırma ile deneme için uygun şekilde yapılandırılmış temiz bir OpenGL uygulamasına yol açacaktır. Her şey derlenirse şöyle görünmelidir:

Şu anda bakacak pek bir şey yok, ama önümüzdeki bölümlerde daha ilginç olacağına söz veriyorum.

Tüm kod parçalarının bir bütün olarak uygulamada bir araya geldiği yeri bulmakta zorluk çekiyorsanız, kaynak kodu buradan kontrol edin ve kod /yorumlar üzerinde dikkatlice çalışın.

Artık renkler hakkında oldukça fazla bilgiye sahip olduğumuza ve ışıklandırmayı denemek için temel bir sahne oluşturduğumuza göre, gerçek sihrin başladığı bir sonraki bölüme geçebiliriz.

Orijinal Kaynak: Colors

Çeviri: Nezihe Sözen

Last updated