OpenGL: Как мне отрисовать отдельные значения RGB с учетом перекрестного произведения, которое содержит общий свет? - PullRequest
1 голос
/ 13 марта 2019

Я работал над собственной реализацией модели затенения в стиле Гуро, и у меня остальная часть работает в основном так, как я хочу, но проблема, с которой я столкнулся, заключается в том, что она показывает только белый свет , Функция calc_color - это место, где выполняется эта операция. Переменная Color представляет собой total light значений R, G и B для данного заданного местоположения. Я назначил Color всем трем массивам, чтобы правильно реализовать затенение, но теперь, когда это завершено, я хотел бы найти способ извлечь значения R, G и B из этого общего значения освещения.

Я пробовал несколько разных вещей, например, брал общий свет, брал процент от значений Light1r и т. Д., Но в итоге он всегда выглядел странным или слишком ярким.

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifdef MAC
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

using namespace std;

//Camera variables
int xangle = -270;
int yangle = 0;

//Control Modes (Rotate mode by default)
int mode = 0;
int lightmode = 0;

//Player Position (Y offset so it would not be straddling the grid)
float cubeX = 0;
float cubeY = 0.5;
float cubeZ = 0;

//Vertex arrays for surface
float surfaceX [12][12];
float surfaceY [12][12];
float surfaceZ [12][12];

//Surface Normal arrays
float Nx[11][11];
float Ny[11][11];
float Nz[11][11];

//Color arrays
float R[11][11];
float G[11][11];
float B[11][11];

//Light position and color variables
float Light1x = 0;
float Light1y = 5;
float Light1z = 0;

float Light1r = 0;
float Light1g = 1;
float Light1b = 0;

float Light2x = -5;
float Light2y = 5;
float Light2z = -5;

float Light2r = 0;
float Light2g = 1;
float Light2b = 0;



//Random number generator
float RandomNumber(float Min, float Max)
{
    return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min;
}

//---------------------------------------
// Initialize surface 
//---------------------------------------
void init_surface()
{
    //Initialize X, select column  
    for (int i = 0; i < 12; i++) 
    {
        //Select row
        //Surface is +1 so the far right normal will be generated correctly     
        for (int j = 0; j < 12; j++)
        {   
            //-5 to compensate for negative coordinate values
            surfaceX[i][j] = i-5;
            //Generate random surface height
            surfaceY[i][j] = RandomNumber(5, 7) - 5;
            //surfaceY[i][j] = 0;
            surfaceZ[i][j] = j-5;
        }
    }
}

void define_normals()
{
    //Define surface normals
    for (int i = 0; i < 11; i++)
    {
        for (int j = 0; j < 11; j++)
        {
            //Get two tangent vectors
            float Ix = surfaceX[i+1][j] - surfaceX[i][j];
            float Iy = surfaceY[i+1][j] - surfaceY[i][j];
            float Iz = surfaceZ[i+1][j] - surfaceZ[i][j];
            float Jx = surfaceX[i][j+1] - surfaceX[i][j];
            float Jy = surfaceY[i][j+1] - surfaceY[i][j];
            float Jz = surfaceZ[i][j+1] - surfaceZ[i][j];

            //Do cross product, inverted for upward normals
            Nx[i][j] = - Iy * Jz + Iz * Jy;
            Ny[i][j] = - Iz * Jx + Ix * Jz;
            Nz[i][j] = - Ix * Jy + Iy * Jx;

            //Original vectors
            //Nx[i][j] = Iy * Jz - Iz * Jy;
            //Ny[i][j] = Iz * Jx - Ix * Jz;
            //Nz[i][j] = Ix * Jy - Iy * Jx;

            float length = sqrt( 
                Nx[i][j] * Nx[i][j] + 
                Ny[i][j] * Ny[i][j] + 
                Nz[i][j] * Nz[i][j]);
            if (length > 0)
            {
                Nx[i][j] /= length;
                Ny[j][j] /= length;
                Nz[i][j] /= length;
            }
        }   
    } 
}

void calc_color()
{
    for (int i = 0; i < 10; i++)
    {           
        for (int j = 0; j < 10; j++)
        {
            //Calculate light vector
            //Light position, hardcoded for now 0,1,1
            float Lx = Light1x - surfaceX[i][j]; 
            float Ly = Light1y - surfaceY[i][j];
            float Lz = Light1z - surfaceZ[i][j];

            float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
            if (length > 0)
            {
                Lx /= length;
                Ly /= length;
                Lz /= length;
                        }

            //std::cout << "Lx: " << Lx << std::endl;   
            //std::cout << "Ly: " << Ly << std::endl;
            //std::cout << "Lz: " << Lz << std::endl;



            //Grab surface normals
            //These are Nx,Ny,Nz due to compiler issues
            float Na = Nx[i][j];
            float Nb = Ny[i][j];
            float Nc = Nz[i][j];

            //std::cout << "Na: " << Na << std::endl;   
            //std::cout << "Nb: " << Nb << std::endl;   
            //std::cout << "Nc: " << Nc << std::endl;

            //Do cross product
            float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);
            std::cout << "Color: " << Color << std::endl;

            //if (Color > 0)
            //{
            //  Color = Color / 100;
            //}
            //Percent of light color
            //float Ramt = (Light1r/2) / Color;
            //float Gamt = (Light1g/2) / Color;
            //float Bamt = (Light1b/2) / Color;
            //R[i][j] = Ramt * Color;
            //G[i][j] = Gamt * Color;
            //B[i][j] = Bamt * Color;

            R[i][j] = Color;
            G[i][j] = Color;
            B[i][j] = Color;    
        }
    }
}

//---------------------------------------
// Init function for OpenGL
//---------------------------------------
void init()
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //Viewing Window Modified
    glOrtho(-7.0, 7.0, -7.0, 7.0, -7.0, 7.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    //Rotates camera
    //glRotatef(30.0, 1.0, 1.0, 1.0);
    glEnable(GL_DEPTH_TEST);

    //Project 3 code
    init_surface();
    define_normals();

    //Shading code
    // glShadeModel(GL_SMOOTH);
    // glEnable(GL_NORMALIZE);

    //X,Y,Z - R,G,B
    // init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
    // init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
    //init_light(GL_LIGHT2, 0, 1, 0, 0.5, 0.5, 0.5);


}

void keyboard(unsigned char key, int x, int y)
{

///TODO: allow user to change color of light

    //Controls
    //Toggle Mode  
    if (key == 'q')
    {
        if(mode == 0)
        {       
            mode = 1;
            std::cout << "Switched to Light mode (" << mode << ")" << std::endl;    
        }
        else if(mode == 1)
        {   
            mode = 0;
            std::cout << "Switched to Rotate mode (" << mode << ")" << std::endl;
        }
    }
    //Toggle light control
    else if (key == 'e' && mode == 1)
    {
        if(lightmode == 0)
        {       
            lightmode = 1;
            std::cout << "Switched to controlling light 2 (" << lightmode << ")" << std::endl;  
        }

        else if(lightmode == 1)
        {       
            lightmode = 0;
            std::cout << "Switched to controlling light 1 (" << lightmode << ")" << std::endl;  
        }
    }

    ////Rotate Camera (mode 0)
    //Up & Down
    else if (key == 's' && mode == 0)
        xangle += 5;
    else if (key == 'w' && mode == 0)
    xangle -= 5;

    //Left & Right
    else if (key == 'a' && mode == 0) 
    yangle -= 5;
    else if (key == 'd' && mode == 0) 
    yangle += 5;

    ////Move Light (mode 1)
    //Forward & Back
    else if (key == 'w' && mode == 1) 
    {
        if (lightmode == 0) 
        {       
            Light1z = Light1z - 1;
            //init_surface();           
            //define_normals();         
            //calc_color();
            //glutPostRedisplay();

        }
        else if (lightmode == 1)
        Light2z = Light2z - 1;
        //init_surface();

    }

    else if (key == 's' && mode == 1)
    {
        if (lightmode == 0)
        Light1z = Light1z + 1;

        else if (lightmode == 1)
        Light2z = Light2z + 1;
    }

    //Strafe
    else if (key == 'd' && mode == 1)
    {
        if (lightmode == 0)
        Light1x = Light1x + 1;

        else if (lightmode == 1)
        Light2x = Light2x + 1;  
    }
    else if (key == 'a' && mode == 1)
    {
        if (lightmode == 0)     
        Light1x = Light1x - 1;
        else if (lightmode == 1)
        Light2x = Light2x - 1;

    }   

    //Up & Down (Cube offset by +0.5 in Y)
    else if (key == 'z' && mode == 1)
    {
        if (lightmode == 0)
        Light1y = Light1y + 1;
        else if (lightmode == 1)
        Light2y = Light2y + 1;
    }
    else if (key == 'x' && mode == 1)
    {
        if (lightmode == 0)
        Light1y = Light1y - 1;
        else if (lightmode == 1)
        Light2y = Light2y - 1;
    }

    //Redraw objects
    glutPostRedisplay();
}


//---------------------------------------
// Display callback for OpenGL
//---------------------------------------
void display()
{       
        // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Rotation Code
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(xangle, 1.0, 0.0, 0.0);
    glRotatef(yangle, 0.0, 1.0, 0.0);

    //Light Code
    // init_material(Ka, Kd, Ks, 100 * Kp, 0.8, 0.6, 0.4);  

    // init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
        // init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);

    // glEnable(GL_LIGHTING);
    //Color Code
    calc_color();

    //Draw the squares, select column  
    for (int i = 0; i <= 9; i++)
    {
        //Select row        
        for (int j = 0; j <= 9; j++)
        {   
            glBegin(GL_POLYGON); 

            //Surface starts at top left
            //Counter clockwise

            glColor3f(R[i][j], G[i][j], B[i][j]);
            std::cout << R[i][j] << " " <<  G[i][j] << " " <<  B[i][j] << endl;
            // glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
            glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);

            glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
            // glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
            glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);

            glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
            // glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
            glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);

            glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
            // glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
            glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);

            glEnd();
        }
    }

    // glDisable(GL_LIGHTING);
    //Draw the normals
    for (int i = 0; i <= 10; i++)
    {
        for (int j = 0; j <= 10; j++)
        {

            glBegin(GL_LINES);
            glColor3f(0.0, 1.0, 1.0);
            float length = 1;
            glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
                glVertex3f(surfaceX[i][j]+length*Nx[i][j], 
                surfaceY[i][j]+length*Ny[i][j], 
                surfaceZ[i][j]+length*Nz[i][j]);
            glEnd();
        }
    }

    //Marking location of lights
    glPointSize(10);
    glBegin(GL_POINTS);
    glColor3f(Light1r, Light1g, Light1b);
    glVertex3f(Light1x, Light1y, Light1z);      
    glEnd();

    glPointSize(10);
    glBegin(GL_POINTS);
    glColor3f(Light2r, Light2g, Light2b);
    glVertex3f(Light2x, Light2y, Light2z);      
    glEnd();

    //+Z = Moving TOWARD camera in opengl
    //Origin point for reference
    glPointSize(10);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);
    glVertex3f(0, 0, 0);        
    glEnd();

    //Assign Color of Lines
    float R = 1;
    float G = 1;
    float B = 1;
    glBegin(GL_LINES);
    glColor3f(R, G, B);

    ////Drawing the grid
    //Vertical lines
    for (int i = 0; i < 11; i++)
    {
        int b = -5 + i;

        glVertex3f(b, 0, -5);
        glVertex3f(b, 0, 5);
    }

    //Horizontal lines
    for (int i = 0; i < 11; i++)
    {
        int b = -5 + i;
        glVertex3f(-5,0,b);
        glVertex3f(5,0,b);

    }

    glEnd();
    glFlush();  
}

//---------------------------------------
// Main program
//---------------------------------------
int main(int argc, char *argv[])
{
    srand(time(NULL));

    //Print Instructions
    std::cout << "Project 3 Controls: " << std::endl;
    std::cout << "q switches control mode" << std::endl;
    std::cout << "w,a,s,d for camera rotation" << std::endl;


    //Required
    glutInit(&argc, argv);
    //Window will default to a different size without
    glutInitWindowSize(500, 500);
    //Window will default to a different position without
    glutInitWindowPosition(250, 250);
    //
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
    //Required
    glutCreateWindow("Project 3");
    //Required, calls display function
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    //Required
    init();
    glutMainLoop();



   return 0;
}

Ответы [ 2 ]

1 голос
/ 14 марта 2019

Обычная формула для расчета рассеянного света - это вычисление Точечного произведения вектора нормали поверхности и вектора от поверхности к источнику света. См. Как работает подделка света на аэротвисте? .

kd = max(0, L dot N)

Чтобы получить цвет света, значения RGB компонентно умножаются на коэффициент диффузии:

(Cr, Cg, Cb) = (LCr, LCg, LCb) * kd

Если имеется несколько источников света, то цвета света суммируются:

(Cr, Cg, Cb) = (LC1r, LC1g, LC1b) * max(0, L1 dot N) + (LC2r, LC2g, LC2b) * max(0, L2 dot N)

Обратите внимание: если поверхность (материал) имеет дополнительный цвет, то этот цвет должен быть умножен на компонент до конечного цвета:

(Cr, Cg, Cb) = (Cr, Cg, Cb) * (CMr, CMg, CMb)

Напишите функцию, которая рассчитывает свет для 1 отдельного источника света и добавьте свет к окончательному цвету:

void add_light_color(int i, int j, float lpx, float lpy, float lpz, float lcr, float lcg, float lcb)
{
    float Lx = lpx - surfaceX[i][j]; 
    float Ly = lpy - surfaceY[i][j];
    float Lz = lpz - surfaceZ[i][j];

    float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
    if (length <= 0.0)
        return;

    float kd = Lx/length * Nx[i][j] + Ly/length * Ny[i][j] + Ly/length * Ny[i][j];
    if ( kd <= 0.0 )
    return; 

    R[i][j] += kd * lcr;
    G[i][j] += kd * lcg;
    B[i][j] += kd * lcb;
}

Перейдите к полю атрибутов, установите каждый цвет (0, 0, 0) и используйте вышеуказанную функцию, чтобы добавить цвет для каждого источника света:

void calc_color()
{
    float lp1[] = {Light1x, Light1y, Light1z};
    float lp2[] = {Light2x, Light2y, Light2z};
    float lc1[] = {Light1r, Light1g, Light1b};
    float lc2[] = {Light2r, Light2g, Light2b};

    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            R[i][j] = G[i][j] = B[i][j] = 0.0;  

            add_light_color(i, j, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
            add_light_color(i, j, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
        }
    }
}

Результат для следующих настроек светлого цвета:

float Light1r = 1;
float Light1g = 0;
float Light1b = 0;

float Light2r = 0;
float Light2g = 1;
float Light2b = 0;

1 голос
/ 14 марта 2019

Я рекомендую написать вашу первую шейдерную программу , которая работает на освещение фрагментов и состоит из Vertex Shader и Fragment Shader .
Программа должна использовать GLSL версии 2.00 ( OpenGL Shading Language 1.20 Спецификация ). Эта программа может получить доступ к атрибутам фиксированной функции с помощью встроенных переменных gl_Vertex, gl_Normal и gl_Color, таких как матрицы фиксированных функций gl_NormalMatrix, gl_ModelViewMatrix и gl_ModelViewProjectionMatrix и функция ftransform().
См. Также Встроенные атрибуты вершин и Hello World в GLSL .
Далее программа должна использовать Uniform Variables для светлых цветов и позиций.

Шейдер Vertex преобразует пространственные координаты и векторы модели, чтобы просмотреть пространство и передать фрагментный шейдер с помощью различных переменных :

std::string vertex_shader = R"(
#version 120

uniform vec3 u_light_pos_1;
uniform vec3 u_light_pos_2;

varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;

void main()
{
    v_pos        = (gl_ModelViewMatrix * gl_Vertex).xyz;
    v_nv         = gl_NormalMatrix * gl_Normal;
    v_color      = gl_Color;
    v_light_pos1 = (gl_ModelViewMatrix * vec4(u_light_pos_1, 1.0)).xyz;
    v_light_pos2 = (gl_ModelViewMatrix * vec4(u_light_pos_2, 1.0)).xyz;
    gl_Position  = ftransform();
}
)";

Фрагмент шейдерной дозы на Фрагмент Light Расчеты в поле зрения:

std::string fragment_shader = R"(
#version 120

varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;

uniform vec3 u_light_col_1;
uniform vec3 u_light_col_2;

void main()
{
    vec3 N  = normalize(v_nv); 
    vec3 L1 = normalize(v_light_pos1 - v_pos);
    vec3 L2 = normalize(v_light_pos2 - v_pos);

    float kd_1 = max(0.0, dot(L1, N));
    float kd_2 = max(0.0, dot(L2, N));

    vec3 light_sum = kd_1 * u_light_col_1 + kd_2 * u_light_col_2;

    gl_FragColor = vec4(v_color.rgb * light_sum, v_color.a);
}
)";

Компилировать этапы шейдера

GLuint generate_shader(GLenum stage, const std::string &source)
{
    GLuint shader_obj = glCreateShader(stage);
    const char *srcCodePtr = source.c_str();
    glShaderSource(shader_obj, 1, &srcCodePtr, nullptr);
    glCompileShader(shader_obj);
    GLint status;
    glGetShaderiv(shader_obj, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE)
    {
        GLint maxLen;
        glGetShaderiv(shader_obj, GL_INFO_LOG_LENGTH, &maxLen);
        std::vector< char >log( maxLen );
        GLsizei len;
        glGetShaderInfoLog(shader_obj, maxLen, &len, log.data());
        std::cout << "compile error:" << std::endl << log.data() << std::endl;
    }
    return shader_obj;
}

и ссылку на программу.

GLuint generate_program(const std::string &vert_sh, const std::string &frag_sh)
{
    std::cout << "compile vertex shader" << std::endl;
    GLuint vert_obj = generate_shader(GL_VERTEX_SHADER, vert_sh);
    std::cout << "compile fragment shader" << std::endl;
    GLuint frag_obj = generate_shader(GL_FRAGMENT_SHADER, frag_sh);

    std::cout << "link shader program" << std::endl;
    GLuint program_obj = glCreateProgram();
    glAttachShader(program_obj, vert_obj);
    glAttachShader(program_obj, frag_obj);
    glLinkProgram(program_obj);
    GLint status;
    glGetProgramiv(program_obj, GL_LINK_STATUS, &status);
    if (status == GL_FALSE)
    {
        GLint maxLen;
        glGetProgramiv(program_obj, GL_INFO_LOG_LENGTH, &maxLen);
        std::vector< char >log( maxLen );
        GLsizei len;
        glGetProgramInfoLog(program_obj, maxLen, &len, log.data());
        std::cout  << "link error:" << std::endl << log.data() << std::endl;
    }

    glDeleteShader(vert_obj);
    glDeleteShader(frag_obj);
    return program_obj;
}

Далее получите единообразные местоположения с помощью glGetUniformLocation в функции init:

GLuint diffuse_prog_obj = 0;
GLint loc_l_pos[] = {-1, -1};
GLint loc_l_col[] = {-1, -1};
void init()
{
    diffuse_prog_obj = generate_program(vertex_shader, fragment_shader);
    loc_l_pos[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_1");
    loc_l_pos[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_2");
    loc_l_col[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_1");
    loc_l_col[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_2");

    // [...]
}

Программа шейдера может использоваться glUseProgram. Униформа устанавливается по glUniform*.
Помимо координат вершин, атрибуты вектора нормали должны быть установлены для каждой вершины, чтобы сделать правильные вычисления света. Но достаточно задать один цвет для всей сетки:

void display()
{
    // [...]

    // install program
    glUseProgram(diffuse_prog_obj);

    // set light positions and colors 
    glUniform3f(loc_l_pos[0], Light1x, Light1y, Light1z);
    glUniform3f(loc_l_pos[1], Light2x, Light2y, Light2z);
    glUniform3f(loc_l_col[0], Light1r, Light1g, Light1b);
    glUniform3f(loc_l_col[1], Light2r, Light2g, Light2b);

    // set object color
    glColor3f(1, 1, 0.5);

    //Draw the squares, select column  
    for (int i = 0; i <= 9; i++)
    {
        //Select row        
        for (int j = 0; j <= 9; j++)
        {   
            glBegin(GL_POLYGON); 

            std::cout << R[i][j] << " " <<  G[i][j] << " " <<  B[i][j] << endl;
            glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
            glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);

            glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
            glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);

            glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
            glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);

            glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
            glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);

            glEnd();
        }
    }

    // invalidate installed program
    glUseProgram(0);

    // [...]
}

Смотрите предварительный просмотр вашей программы с предложениями:

...