जीएलएसएल संस्करण 330 के साथ एक स्काईबॉक्स लागू करना


14

मैं OpenGL 3.3 और GLSL संस्करण 330 के साथ काम करने के लिए एक स्काईबॉक्स प्राप्त करने की कोशिश कर रहा हूं।

मुझे वेब पर कहीं भी पूरी तरह से आधुनिक ओजीएल स्काईबॉक्स ट्यूटोरियल नहीं मिला, इसलिए मैंने एक पुराने एक ( glVertexAttribPointer()इसके बजाय का उपयोग करके) को आधुनिक बनायाgl_Vertex ) को । यह ज्यादातर काम कर रहा है, लेकिन 2 प्रमुख विवरणों के लिए:

स्काईबॉक्स आकाश त्रिकोण की तरह अधिक हैं, और बनावट बुरी तरह से विकृत और फैली हुई हैं (वे स्टार फ़ील्ड होने चाहिए, मुझे एक काली पृष्ठभूमि पर लाइनें मिलती हैं)। मुझे यकीन है कि यह 99% है क्योंकि मैंने पुराने ट्यूटोरियल को पूरी तरह से सही तरीके से पोर्ट नहीं किया है।

यहाँ मेरा स्काईबॉक्स क्लास है:

static ShaderProgram* cubeMapShader = nullptr;

static const GLfloat vertices[] = 
{
    1.0f, -1.0f,  1.0f,
    1.0f,  1.0f,  1.0f,
    1.0f,  1.0f, -1.0f,
    -1.0f, -1.0f,  1.0f,
    -1.0f, -1.0f, -1.0f,
    -1.0f,  1.0f, -1.0f,
    -1.0f,  1.0f,  1.0f,
    -1.0f,  1.0f, -1.0f,
    1.0f,  1.0f, -1.0f,
    1.0f,  1.0f,  1.0f,
    -1.0f,  1.0f,  1.0f,
    -1.0f, -1.0f,  1.0f,
    1.0f, -1.0f,  1.0f,
    1.0f, -1.0f, -1.0f,
    -1.0f, -1.0f, -1.0f,
    1.0f, -1.0f,  1.0f,
    -1.0f, -1.0f,  1.0f,
    -1.0f,  1.0f,  1.0f,
    1.0f,  1.0f,  1.0f,
    -1.0f, -1.0f, -1.0f,
    1.0f, -1.0f, -1.0f,
    1.0f,  1.0f, -1.0f,
    -1.0f,  1.0f, -1.0f
};

Skybox::Skybox(const char* xp, const char* xn, const char* yp, const char* yn, const        char* zp, const char* zn)
{
if (cubeMapShader == nullptr)
    cubeMapShader = new ShaderProgram("cubemap.vert", "cubemap.frag");

    texture = SOIL_load_OGL_cubemap(xp, xn, yp, yn, zp, zn, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);

    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

    glGenVertexArrays(1, &vaoID);
    glBindVertexArray(vaoID);
    glGenBuffers(1, &vboID);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glBindVertexArray(0);

    scale = 1.0f;
}

Skybox::~Skybox()
{

}

void Skybox::Render()
{
    ShaderProgram::SetActive(cubeMapShader);
    glDisable(GL_DEPTH_TEST);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    cubeMapShader->Uniform1i("SkyTexture", 0);
    cubeMapShader->UniformVec3("CameraPosition", Camera::ActiveCameraPosition());
    cubeMapShader->UniformMat4("MVP", 1, GL_FALSE, Camera::GetActiveCamera()->GetProjectionMatrix() * Camera::GetActiveCamera()->GetViewMatrix() * glm::mat4(1.0));
    glBindVertexArray(vaoID);
    glDrawArrays(GL_QUADS, 0, 24);
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

वर्टेक्स शेडर:

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

uniform vec3 CameraPosition;
uniform mat4 MVP;

out vec3 Position;

void main()
{
    Position = Vertex.xyz;
    gl_Position = MVP * vec4(Vertex.xyz + CameraPosition, 1.0);
}

टुकड़े टुकड़े करना:

#version 330 compatibility

uniform samplerCube SkyTexture;

in vec3 Position;

void main()
{
    gl_FragColor = textureCube(SkyTexture, Position);
}

यहाँ glitches का एक उदाहरण है। अगर कोई भी देख सकता है जो जीएलएसएल को अच्छी तरह से जानता है (मैं अभी भी इसे सीख रहा हूं), या स्काईबॉक्स, मैं आपकी किसी भी मदद की सराहना करूंगा। इसके अलावा, अगर आप मुझे सिखा सकते हैं कि खंड-खंड में गैर-पदावनत कार्यों का उपयोग कैसे करें तो मुझे glsl 330 की संगतता प्रोफ़ाइल का उपयोग करने की आवश्यकता नहीं है।


EDIT: तुरंत स्ट्रेचिंग टैक्स्चर के साथ समस्या का पता चला: मैं वर्टेकर शेडर के Position = Vertex.xyxबजाय उपयोग कर रहा था Position = Vertex.xyz। उफ़। लेकिन त्रिकोण त्रुटि अभी भी मौजूद है।


1
क्यूबैप बनावट के साथ एक स्काईबॉक्स को प्रस्तुत करने के लिए आपको केवल 4 कोने (फुलस्क्रीन क्वाड) की आवश्यकता होती है। आपको बस एक वर्टेक्स शेडर की आवश्यकता होती है जो कैमरा और प्रोजेक्शन के आधार पर सही बनावट के निर्देशांक की गणना करता है।
एमएसएल

यह एक घिनौना मुद्दा हो सकता है। क्या आपने पूर्ण बॉक्स प्राप्त करने की कोशिश करने और देखने के लिए बैकलेस कुलिंग को अक्षम करने का प्रयास किया है?
pwny

@pwny, मैंने ऐसा नहीं सोचा था। मैंने इसकी कोशिश की, और यह काम नहीं किया, लेकिन मैं देख सकता हूं कि इसे कैसे फेंक दिया जा सकता है। सलाह के लिये धन्यवाद।
sm81095

@msell, मैंने इस दृष्टिकोण के बारे में सुना है, लेकिन मुझे इसके लिए ऑनलाइन एक ट्यूटोरियल नहीं मिला, और मैं अभी भी glsl सीखने की प्रक्रिया में हूं। यदि आप एक उदाहरण या उदाहरण के लिए एक लिंक प्रदान कर सकते हैं कि यह कैसे करना है, तो मैं बहुत सराहना करता हूं।
sm81095

जवाबों:


29

हालांकि यह जवाब नहीं बताता है कि आपके दृष्टिकोण में क्या गलत है, यह स्काईबॉक्स को प्रस्तुत करने का एक सरल तरीका प्रस्तुत करता है।

पारंपरिक तरीका (बनावट वाला घन)

स्काईबॉक्स बनाने के लिए एक सीधा तरीका कैमरा स्थिति पर केंद्रित एक बनावट क्यूब को प्रस्तुत करना है। क्यूब के प्रत्येक चेहरे में दो त्रिकोण और एक 2 डी बनावट (या एटलस का हिस्सा) होता है। बनावट के निर्देशांक के कारण प्रत्येक चेहरे को स्वयं के कोने की आवश्यकता होती है। इस दृष्टिकोण में आसन्न चेहरों के सीम में समस्याएं हैं, जहां बनावट मूल्यों को ठीक से प्रक्षेपित नहीं किया गया है।

क्यूबैप बनावट के साथ घन

परंपरागत तरीके की तरह, कैमरे के चारों ओर एक बनावट वाला घन प्रस्तुत किया गया है। छह 2D बनावट का उपयोग करने के बजाय, एक एकल क्यूबैप बनावट का उपयोग किया जाता है। क्यूब के अंदर कैमरा केंद्रित होने के कारण, वर्टेब मैपअप वैक्टर के साथ एक से एक मैप का समन्वय करता है। इस प्रकार जाल डेटा के लिए बनावट निर्देशांक की आवश्यकता नहीं होती है और अनुक्रमणिका बफर का उपयोग करके चेहरे के बीच कोने को साझा किया जा सकता है।

GL_TEXTURE_CUBE_MAP_SEAMLESS सक्षम होने पर यह दृष्टिकोण सीम की समस्या को भी ठीक करता है।

सरल (बेहतर) तरीका

क्यूब को रेंडर करते समय और कैमरा उसके अंदर होता है, पूरा व्यूपोर्ट भर जाता है। किसी भी समय स्काईबॉक्स के पांच चेहरे आंशिक रूप से दिखाई दे सकते हैं। घन चेहरों के त्रिभुजों को प्रक्षेपित किया जाता है और व्यूपोर्ट में क्लिप किया जाता है और क्यूबैप नमूने वाले वैक्टर को कोने के बीच प्रक्षेपित किया जाता है। यह काम अनावश्यक है।

पूरे व्यूपोर्ट को भरने वाले एक ही क्वाड को भरना और कोनों में क्यूबैप सैंपलिंग वैक्टर की गणना करना संभव है। क्यूंकि क्यूबैप सैंपल वाले वैक्टर शीर्ष निर्देशांक से मेल खाते हैं, उनकी गणना विश्व अंतरिक्ष के व्यूपोर्ट निर्देशांक को अनप्रोजेक्ट करके की जा सकती है। यह प्रोजेक्टिंग वर्ल्ड के विपरीत है जो व्यूपोर्ट में समन्वय करता है और मैट्रिसेस को इन्वर्ट करके प्राप्त किया जा सकता है। यह भी सुनिश्चित करें कि आप या तो z- बफर राइट को डिसेबल करते हैं या एक वैल्यू लिखते हैं जो काफी दूर है।

नीचे शीर्ष दाता है जो इसे पूरा करता है:

#version 330
uniform mat4 uProjectionMatrix;
uniform mat4 uWorldToCameraMatrix;

in vec4 aPosition;

smooth out vec3 eyeDirection;

void main() {
    mat4 inverseProjection = inverse(uProjectionMatrix);
    mat3 inverseModelview = transpose(mat3(uWorldToCameraMatrix));
    vec3 unprojected = (inverseProjection * aPosition).xyz;
    eyeDirection = inverseModelview * unprojected;

    gl_Position = aPosition;
} 

aPosition शीर्ष निर्देशांक है {-1,-1; 1,-1; 1,1; -1,1} । Shader की गणना करता हैeyeDirection मॉडल-व्यू-प्रोजेक्शन मैट्रिक्स के व्युत्क्रम के साथ करता है। हालांकि उलटा प्रक्षेपण और विश्व-से-कैमरा मेट्रिसेस के लिए विभाजित है। ऐसा इसलिए है क्योंकि कैमरे की स्थिति को खत्म करने के लिए कैमरा मैट्रिक्स का केवल 3x3 भाग का उपयोग किया जाना चाहिए। यह कैमरे को स्काईबॉक्स के केंद्र में संरेखित करता है। इसके अलावा मेरे कैमरे में कोई स्केलिंग या शीयरिंग नहीं है, उलटा को ट्रांसपोज़ेशन में सरल बनाया जा सकता है। प्रोजेक्शन मैट्रिक्स का व्युत्क्रम एक महंगा ऑपरेशन है और इसे पूर्व-निर्धारित किया जा सकता है, लेकिन जैसा कि इस कोड को आमतौर पर प्रति फ्रेम केवल चार बार वर्टेक्स शेडर द्वारा निष्पादित किया जाता है, यह आमतौर पर एक गैर-मुद्दा है।

टुकड़ा shader बस eyeDirectionवेक्टर का उपयोग कर एक बनावट लुकअप करता है :

#version 330
uniform samplerCube uTexture;

smooth in vec3 eyeDirection;

out vec4 fragmentColor;

void main() {
    fragmentColor = texture(uTexture, eyeDirection);
}

ध्यान दें कि संगतता मोड से छुटकारा पाने के लिए आपको textureCubeबस के साथ बदलने textureऔर आउटपुट चर को स्वयं निर्दिष्ट करने की आवश्यकता है ।


मुझे लगता है कि आपको यह भी उल्लेख करना चाहिए कि मैट्रिक्स उलटा एक महंगा प्रक्रिया है, इसलिए यह क्लाइंट-साइड कोड में बेहतर होता है।
अक्कलतार

1
फुलस्क्रीन क्वाड के 4 वर्ट्स के लिए मुझे नहीं लगता कि हमें इनवर्टर की लागत के बारे में बहुत चिंता करने की ज़रूरत है (विशेष रूप से GPU इसे 4 बार करने के बावजूद अभी भी सीपीयू के एक बार करने की तुलना में तेजी से संभव होगा)।
मैक्सिमस मिनिमस

1
बस लोगों के लिए एक उपयोगी नोट, GLSL ES 1.0 (GL ES 2.0 के लिए उपयोग किया जाता है) लागू नहीं होता हैinverse()
स्टीवन लू

कैमरा के एमवीपी uWorldToCameraMatrix वस्तु है?
सिदार

@Sidar नहीं, यह सिर्फ ModelView मैट्रिक्स है, प्रोजेक्शन अलग है।
एमएसल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.