python - 如何在python上制作镜面光照来实现Phong光照?

标签 python opengl pyopengl phong specular

我需要你的帮助。使用 python,我必须通过 phong 模型实现三维形状的照明。我的图形是三角二十面体。我能够构建这个形状,找到顶点的坐标,并计算每个面的法线。

我使用了 pygame、pyopengl。

为了实现phong光照模型,我设法制作了环境光照和漫反射光照,但我不知道要使用什么函数来进行镜面光照。

我尝试应用具有不同参数的函数,例如 glMaterialfv(),但它对我来说没有成功。

这是我的代码:

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

verticies = (
    (0, - 0, 1.15894198417663574),
    (0.63384598493576048, -0.63384598493576048, 0.63384598493576048),
    (0.81949597597122192, 0, 0.81949597597122192),
    (1.15894198417663574, 0, -0),
    (0.81949597597122192, 0.81949597597122192, -0),
    (0.81949597597122192, 0, -0.81949597597122192),
    (0.63384598493576048, 0.63384598493576048, 0.63384598493576048),
    (0.81949597597122192, -0.81949597597122192, 0),
    (-0, 0.81949597597122192, 0.81949597597122192),
    (-0.81949597597122192, 0, 0.81949597597122192),
    (0.63384598493576048, 0.63384598493576048, -0.63384598493576048),
    (0, 0, -1.15894198417663574),
    (0, -0.81949597597122192, -0.81949597597122192),
    (-0.81949597597122192, 0, -0.81949597597122192),
    (0, 0.81949597597122192, -0.81949597597122192),
    (0.63384598493576048, -0.63384598493576048, -0.63384598493576048),
    (-0, 1.15894198417663574, 0),
    (-0.81949597597122192, 0.81949597597122192, 0),
    (-1.15894198417663574, 0, 0),
    (-0.63384598493576048, 0.63384598493576048, 0.63384598493576048),
    (0, -1.15894198417663574, 0),
    (-0.81949597597122192, -0.81949597597122192, 0),
    (0, -0.81949597597122192, 0.81949597597122192),
    (-0.63384598493576048, -0.63384598493576048, 0.63384598493576048),
    (-0.63384598493576048, 0.63384598493576048, -0.63384598493576048),
    (-0.63384598493576048, -0.63384598493576048, -0.63384598493576048),
    )

surfaces = (
    (20, 21, 25, 12),
    (21, 25, 13, 18),
    (17, 24, 14, 16),
    (18, 13, 24, 17),
    (16, 14, 10, 4),
    (3, 5, 15, 7),
    (0, 2, 6, 8),
    (0, 2, 1, 22),
    (0, 22, 23, 9),
    (0, 9, 19, 8),
    (13, 11, 12, 25),
    (11, 13, 24, 14),
    (11, 14, 10, 5),
    (11, 5, 15, 12),
    (3, 5, 10, 4),
    (17, 18, 9, 19),
    (17, 19, 8, 16),
    (16, 8, 6, 4),
    (3, 2, 6, 4),
    (3, 2, 1, 7),
    (7, 1, 22, 20),
    (20, 21, 23, 22),
    (9, 23, 21, 18),
    (15, 7, 20, 12),
)

normals = [
    (-0.35740624923526854, -0.8628558767968414, -0.3574080425574267),
    (-0.8628548655932644, -0.3574083665235253, -0.3574083665235253),
    (-0.3574083665235253, 0.8628548655932644, -0.3574083665235253),
    (-0.8628558767968414, 0.3574080425574267, -0.35740624923526854),
    (0.3574080425574267, 0.8628558767968414, -0.35740624923526854),
    (0.8628558767968414, -0.3574080425574267, -0.35740624923526854),
    (0.35740624923526854, 0.3574080425574267, 0.8628558767968414),
    (0.35740624923526854, -0.3574080425574267, 0.8628558767968414),
    (-0.3574080425574267, -0.35740624923526854, 0.8628558767968414),
    (-0.35740624923526854, 0.3574080425574267, 0.8628558767968414),
    (-0.35740647831364963, -0.35740647831364963, -0.8628564298415289),
    (-0.35740624923526854, 0.3574080425574267, -0.8628558767968414),
    (0.3574080425574267, 0.35740624923526854, -0.8628558767968414),
    (0.35740624923526854, -0.3574080425574267, -0.8628558767968414),
    (0.8628558767968414, 0.3574080425574267, -0.35740624923526854),
    (-0.8628564298415289, 0.35740647831364963, 0.35740647831364963),
    (-0.3574083665235253, 0.8628548655932644, 0.3574083665235253),
    (0.3574080425574267, 0.8628558767968414, 0.35740624923526854),
    (0.8628558767968414, 0.3574080425574267, 0.35740624923526854),
    (0.8628558767968414, -0.3574080425574267, 0.35740624923526854),
    (0.3574083665235253, -0.8628548655932644, 0.3574083665235253),
    (-0.35740624923526854, -0.8628558767968414, 0.3574080425574267),
    (-0.8628548655932644, -0.3574083665235253, 0.3574083665235253),
    (0.35740624923526854, -0.8628558767968414, -0.3574080425574267)
]

colors = (
    (1,1,1),
    (0,1,0),
    (0,0,1),
    (0,1,0),
    (0,0,1),
    (1,0,1),
    (0,1,0),
    (1,0,1),
    (0,1,0),
    (0,0,1),
    )

edges = (
    (16, 17),
    (17, 18),
    (18, 21),
    (20, 21),
    (3, 4),
    (4, 16),
    (7, 3),
    (20, 7),
    (0, 2),
    (0, 9),
    (0, 22),
    (0, 8),
    (11, 13),
    (11, 12),
    (11, 14),
    (2, 3),
    (8, 16),
    (9, 18),
    (22, 20),
    (2, 1),
    (1, 22),
    (1, 7),
    (5, 11),
    (5, 15),
    (15, 12),
    (15, 7),
    (5, 3),
    (12, 20),
    (16, 14),
    (22, 23),
    (23, 9),
    (23, 21),
    (13, 24),
    (14, 24),
    (17, 24),
    (13, 25),
    (12, 25),
    (25, 21),
    (13, 18),
    (8, 6),
    (2, 6),
    (6, 4),
    (10, 4),
    (14, 10),
    (5, 10),
    (17, 19),
    (19, 9),
    (19, 8),
    )


def Cube():
    glBegin(GL_QUADS)
    for i_surface, surface in enumerate(surfaces):
        x = 0
        glNormal3fv(normals[i_surface])
        for vertex in surface:
            #x+=1
            glColor3fv(colors[x])
            glVertex3fv(verticies[vertex])
    glEnd()

    glColor3fv(colors[0])
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()


def main():
    global surfaces

    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    clock = pygame.time.Clock()

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

    glMatrixMode(GL_MODELVIEW)
    glTranslatef(0, 0, -5)

    # Источник света - "от нас"
    glLight(GL_LIGHT0, GL_POSITION,  (0, 0, 1, 0.4))
    
    

    # Ambient lighting
    glLightfv(GL_LIGHT0, GL_AMBIENT, (0, 0, 0, 1))
    # Diffuse lighting
    glLightfv(GL_LIGHT0, GL_DIFFUSE, (0, 0.5, 0.1, 0))

#---------------------------------Specular Lighting------------It does not work!!!-----------
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (1,1,1,0))
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 128)
#--------------------------------------------------------------------------------------------

    glEnable(GL_DEPTH_TEST)

    while True:
        # Обрабатываем события
        for event in pygame.event.get():
            # Если нажимаем крестик на окошке - выходим
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE )
        #glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR)

       
        keys = pygame.key.get_pressed()

        if keys[pygame.K_LEFT]:
            glRotatef(10, 0, 1, 0)
        elif keys[pygame.K_RIGHT]:
            glRotatef(-10, 0, 1, 0)
        elif keys[pygame.K_UP]:
            glRotatef(10, 1, 0, 0)
        elif keys[pygame.K_DOWN]:
            glRotatef(-10, 1, 0, 0)

      
        Cube()

        glDisable(GL_LIGHT0)
        glDisable(GL_LIGHTING)
        glDisable(GL_COLOR_MATERIAL)

        pygame.display.flip()
        clock.tick(20)

if __name__ == '__main__':
    main()

最佳答案

您的代码中的所有内容都是正确的。使用旧版 OpenGL 固定功能管道,这是您可以获得的最佳结果。
固定功能管道使用 Blinn–Phong reflection model 。然而,Gouraud Shading而不是Phong Shading用来。虽然 Phong 着色通常是指按片段执行光照计算的技术,但在 Gouraud 着色中,光照计算是按顶点执行的。计算出的光沿(三角形)Primitives 进行插值.
在镜面高光的情况下,光分布不是线性的,无法通过线性插值来计算。效果扭曲或完全丢失。
请参阅what the difference between phong shading and gouraud shading?OpenGL Lighting on texture plane is not working .

可以通过将网格分割为小三角形来改善照明。这会导致为更多点(顶点)计算光线,并且插值效果较小。

现在,光线是按片段计算的(Phong 阴影)。为此,您需要实现 Shader程序。请参阅GLSL fixed function fragment program replacement .

对于单个 Stack Overflow 答案来说,详细描述这一切太过宽泛。我建议您阅读优秀的 OpenGL 教程。例如:Python Opengl (我最喜欢的是 c++ LearnOpenGL )。


要为您的特定旧代码实现 Phong Shading,您需要编写 version 1.10 GLSL着色器。有关一个很好的示例,请参阅 Per Fragment Lighting 。您需要对着色器程序进行一些调整才能使颜色 Material 发挥作用。

顶点着色器

varying vec3 vN;
varying vec3 v;
varying vec4 color;
void main(void)  
{     
   v = vec3(gl_ModelViewMatrix * gl_Vertex);       
   vN = normalize(gl_NormalMatrix * gl_Normal);
   color = gl_Color;
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  
}

片段着色器

varying vec3 vN;
varying vec3 v; 
varying vec4 color;
#define MAX_LIGHTS 1 
void main (void) 
{ 
   vec3 N = normalize(vN);
   vec4 finalColor = vec4(0.0, 0.0, 0.0, 0.0);
   
   for (int i=0;i<MAX_LIGHTS;i++)
   {
      vec3 L = normalize(gl_LightSource[i].position.xyz - v); 
      vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0) 
      vec3 R = normalize(-reflect(L,N)); 
   
      vec4 Iamb = gl_LightSource[i].ambient; 
      vec4 Idiff = gl_LightSource[i].diffuse * max(dot(N,L), 0.0);
      Idiff = clamp(Idiff, 0.0, 1.0); 
      vec4 Ispec = gl_LightSource[i].specular * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
      Ispec = clamp(Ispec, 0.0, 1.0); 
   
      finalColor += Iamb + Idiff + Ispec;
   }
   gl_FragColor = color * finalColor; 
}

使用 PyOpenGL OpenGL.GL.shaders编译和链接着色器的模块:

def main():
    global surfaces, program

    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    clock = pygame.time.Clock()

    program = compileProgram( 
        compileShader(vertex_shader, GL_VERTEX_SHADER),
        compileShader(fragment_shader, GL_FRAGMENT_SHADER))

    # [...]

在绘制多边形之前安装着色器并启用照明,在绘制线框之前禁用照明。例如:

def Cube():

    glEnable(GL_POLYGON_OFFSET_FILL)
    glPolygonOffset(1.0, 1.0)
    glEnable(GL_LIGHTING)  

    glUseProgram(program)
    glBegin(GL_QUADS)
    for i_surface, surface in enumerate(surfaces):
        x = 0
        glNormal3fv(normals[i_surface])
        for vertex in surface:
            #x+=1
            glColor3fv(colors[x])
            glVertex3fv(verticies[vertex])
    glEnd()

    glDisable(GL_LIGHTING)
    glDisable(GL_POLYGON_OFFSET_FILL)

    glUseProgram(0)
    glColor3fv(colors[0])
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()

完整示例:

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.shaders import *

vertex_shader = """
varying vec3 vN;
varying vec3 v;
varying vec4 color;
void main(void)  
{     
   v = vec3(gl_ModelViewMatrix * gl_Vertex);       
   vN = normalize(gl_NormalMatrix * gl_Normal);
   color = gl_Color;
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  
}
"""

fragment_shader = """
varying vec3 vN;
varying vec3 v; 
varying vec4 color;
#define MAX_LIGHTS 1 
void main (void) 
{ 
   vec3 N = normalize(vN);
   vec4 finalColor = vec4(0.0, 0.0, 0.0, 0.0);
   
   for (int i=0;i<MAX_LIGHTS;i++)
   {
      vec3 L = normalize(gl_LightSource[i].position.xyz - v); 
      vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0) 
      vec3 R = normalize(-reflect(L,N)); 
   
      vec4 Iamb = gl_LightSource[i].ambient; 
      vec4 Idiff = gl_LightSource[i].diffuse * max(dot(N,L), 0.0);
      Idiff = clamp(Idiff, 0.0, 1.0); 
      vec4 Ispec = gl_LightSource[i].specular * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
      Ispec = clamp(Ispec, 0.0, 1.0); 
   
      finalColor += Iamb + Idiff + Ispec;
   }
   gl_FragColor = color * finalColor; 
}
"""

verticies = (
    (0, - 0, 1.15894198417663574),
    (0.63384598493576048, -0.63384598493576048, 0.63384598493576048),
    (0.81949597597122192, 0, 0.81949597597122192),
    (1.15894198417663574, 0, -0),
    (0.81949597597122192, 0.81949597597122192, -0),
    (0.81949597597122192, 0, -0.81949597597122192),
    (0.63384598493576048, 0.63384598493576048, 0.63384598493576048),
    (0.81949597597122192, -0.81949597597122192, 0),
    (-0, 0.81949597597122192, 0.81949597597122192),
    (-0.81949597597122192, 0, 0.81949597597122192),
    (0.63384598493576048, 0.63384598493576048, -0.63384598493576048),
    (0, 0, -1.15894198417663574),
    (0, -0.81949597597122192, -0.81949597597122192),
    (-0.81949597597122192, 0, -0.81949597597122192),
    (0, 0.81949597597122192, -0.81949597597122192),
    (0.63384598493576048, -0.63384598493576048, -0.63384598493576048),
    (-0, 1.15894198417663574, 0),
    (-0.81949597597122192, 0.81949597597122192, 0),
    (-1.15894198417663574, 0, 0),
    (-0.63384598493576048, 0.63384598493576048, 0.63384598493576048),
    (0, -1.15894198417663574, 0),
    (-0.81949597597122192, -0.81949597597122192, 0),
    (0, -0.81949597597122192, 0.81949597597122192),
    (-0.63384598493576048, -0.63384598493576048, 0.63384598493576048),
    (-0.63384598493576048, 0.63384598493576048, -0.63384598493576048),
    (-0.63384598493576048, -0.63384598493576048, -0.63384598493576048),
    )

surfaces = (
    (20, 21, 25, 12),
    (21, 25, 13, 18),
    (17, 24, 14, 16),
    (18, 13, 24, 17),
    (16, 14, 10, 4),
    (3, 5, 15, 7),
    (0, 2, 6, 8),
    (0, 2, 1, 22),
    (0, 22, 23, 9),
    (0, 9, 19, 8),
    (13, 11, 12, 25),
    (11, 13, 24, 14),
    (11, 14, 10, 5),
    (11, 5, 15, 12),
    (3, 5, 10, 4),
    (17, 18, 9, 19),
    (17, 19, 8, 16),
    (16, 8, 6, 4),
    (3, 2, 6, 4),
    (3, 2, 1, 7),
    (7, 1, 22, 20),
    (20, 21, 23, 22),
    (9, 23, 21, 18),
    (15, 7, 20, 12),
)

normals = [
    (-0.35740624923526854, -0.8628558767968414, -0.3574080425574267),
    (-0.8628548655932644, -0.3574083665235253, -0.3574083665235253),
    (-0.3574083665235253, 0.8628548655932644, -0.3574083665235253),
    (-0.8628558767968414, 0.3574080425574267, -0.35740624923526854),
    (0.3574080425574267, 0.8628558767968414, -0.35740624923526854),
    (0.8628558767968414, -0.3574080425574267, -0.35740624923526854),
    (0.35740624923526854, 0.3574080425574267, 0.8628558767968414),
    (0.35740624923526854, -0.3574080425574267, 0.8628558767968414),
    (-0.3574080425574267, -0.35740624923526854, 0.8628558767968414),
    (-0.35740624923526854, 0.3574080425574267, 0.8628558767968414),
    (-0.35740647831364963, -0.35740647831364963, -0.8628564298415289),
    (-0.35740624923526854, 0.3574080425574267, -0.8628558767968414),
    (0.3574080425574267, 0.35740624923526854, -0.8628558767968414),
    (0.35740624923526854, -0.3574080425574267, -0.8628558767968414),
    (0.8628558767968414, 0.3574080425574267, -0.35740624923526854),
    (-0.8628564298415289, 0.35740647831364963, 0.35740647831364963),
    (-0.3574083665235253, 0.8628548655932644, 0.3574083665235253),
    (0.3574080425574267, 0.8628558767968414, 0.35740624923526854),
    (0.8628558767968414, 0.3574080425574267, 0.35740624923526854),
    (0.8628558767968414, -0.3574080425574267, 0.35740624923526854),
    (0.3574083665235253, -0.8628548655932644, 0.3574083665235253),
    (-0.35740624923526854, -0.8628558767968414, 0.3574080425574267),
    (-0.8628548655932644, -0.3574083665235253, 0.3574083665235253),
    (0.35740624923526854, -0.8628558767968414, -0.3574080425574267)
]

colors = (
    (1,1,1),
    (0,1,0),
    (0,0,1),
    (0,1,0),
    (0,0,1),
    (1,0,1),
    (0,1,0),
    (1,0,1),
    (0,1,0),
    (0,0,1),
    )

edges = (
    (16, 17),
    (17, 18),
    (18, 21),
    (20, 21),
    (3, 4),
    (4, 16),
    (7, 3),
    (20, 7),
    (0, 2),
    (0, 9),
    (0, 22),
    (0, 8),
    (11, 13),
    (11, 12),
    (11, 14),
    (2, 3),
    (8, 16),
    (9, 18),
    (22, 20),
    (2, 1),
    (1, 22),
    (1, 7),
    (5, 11),
    (5, 15),
    (15, 12),
    (15, 7),
    (5, 3),
    (12, 20),
    (16, 14),
    (22, 23),
    (23, 9),
    (23, 21),
    (13, 24),
    (14, 24),
    (17, 24),
    (13, 25),
    (12, 25),
    (25, 21),
    (13, 18),
    (8, 6),
    (2, 6),
    (6, 4),
    (10, 4),
    (14, 10),
    (5, 10),
    (17, 19),
    (19, 9),
    (19, 8),
    )


def Cube():

    glEnable(GL_POLYGON_OFFSET_FILL)
    glPolygonOffset(1.0, 1.0)
    glEnable(GL_LIGHTING)  

    glUseProgram(program)
    glBegin(GL_QUADS)
    for i_surface, surface in enumerate(surfaces):
        x = 0
        glNormal3fv(normals[i_surface])
        for vertex in surface:
            #x+=1
            glColor3fv(colors[x])
            glVertex3fv(verticies[vertex])
    glEnd()

    glDisable(GL_LIGHTING)
    glDisable(GL_POLYGON_OFFSET_FILL)

    glUseProgram(0)
    glColor3fv(colors[0])
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()


def main():
    global surfaces, program

    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    clock = pygame.time.Clock()

    program = compileProgram( 
        compileShader(vertex_shader, GL_VERTEX_SHADER),
        compileShader(fragment_shader, GL_FRAGMENT_SHADER))

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

    glMatrixMode(GL_MODELVIEW)
    glTranslatef(0, 0, -5)

    # Источник света - "от нас"
    glLight(GL_LIGHT0, GL_POSITION,  (0, 0, 1, 0.4))
    # Ambient lighting
    glLightfv(GL_LIGHT0, GL_AMBIENT, (0.2, 0.2, 0.2, 1))
    # Diffuse lighting
    glLightfv(GL_LIGHT0, GL_DIFFUSE, (0, 0.5, 0.1, 0))

#---------------------------------Specular Lighting------------It does not work!!!-----------
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (1,1,1,0))
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 128)
#--------------------------------------------------------------------------------------------

    glEnable(GL_DEPTH_TEST)

    while True:
        # Обрабатываем события
        for event in pygame.event.get():
            # Если нажимаем крестик на окошке - выходим
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
        #glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR)

        keys = pygame.key.get_pressed()

        if keys[pygame.K_LEFT]:
            glRotatef(5, 0, 1, 0)
        elif keys[pygame.K_RIGHT]:
            glRotatef(-5, 0, 1, 0)
        elif keys[pygame.K_UP]:
            glRotatef(5, 1, 0, 0)
        elif keys[pygame.K_DOWN]:
            glRotatef(-5, 1, 0, 0)

        Cube()

        glDisable(GL_LIGHT0)
        glDisable(GL_LIGHTING)
        glDisable(GL_COLOR_MATERIAL)

        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()

关于python - 如何在python上制作镜面光照来实现Phong光照?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67544996/

相关文章:

python - 自定义目标函数 Keras

python - 根据列表替换字符串中的子字符串

Python、py2exe 和 pyopengl 导入错误 : No module named win32

python - 如何给三角形添加纹理?

python - 自定义Python线程池同步

python - 你如何在 python 上的 Opencv 中使用 Amaze

c++ - 如何使用 C++/OpenGL 加载透明背景的图标并正确显示?

python - 使用 python 的 OpenGL API,如何对元素索引数组的子集执行 glDrawElements?

opengl - glBufferData 如何知道要处理哪个 VBO?

python - 使用 ctypes.c_void_p 作为 glTexImage2D 的输入?