OpenGL覚え書き5 シェーディング

光源から照射された物体を描画するための処理をシェーディングと呼びます。実際のモデルでは、光源から直接物体に到達する光だけでなく、間接的に物体を照らす光や、物体表面で反射・透過した光などが存在します。

環境光(ambient light)

光源から周囲と相互作用した後に物体に照射される光です。

反射光(reflected light)

物質の表面で反射した光のことです。
反射光は拡散反射(diffuse reflection)による成分と鏡面反射(specular reflection)による成分が含まれます。拡散反射とは、全ての方向に均等に反射する場合です。一方鏡面反射とは、鏡の表面のように反射光の強さが特定の方向に依存するような場合を指します。鏡面反射成分がない反射面を完全拡散面と呼び、どの方向から見ても同じ強度の光になります。
他にも透過光散乱光など様々なタイプの光が考えられます。

光源の追加

光源を追加するにはglEnableに引数としてGL_LIGHT0〜GL_LIGHT7を指定します。

glEnable(GL_LIGHT0)

光源のパラメータ設定

glLightfvに設定したいパラメータを引数で指定して、RGBA値を入力します。例えば、環境光を設定するならGL_AMBIENTを指定します。
その他にも、GL_DIFFUSE(拡散光)、GL_SPECULAR(鏡面反射光)、GL_POSITION(光源の位置)などを設定することができます。GL_SPOT〜というコマンドではスポットライト型の光源として各種パラメータを設定することができます。

ambient = [0.7,0.7,0.9,1.0]
glLightfv(GL_LIGHT0,GL_AMBIENT,ambient)

シェーディング(陰影付け)機能追加

GL_LIGHTINGフラグを指定しましょう。

glEnable(GL_LIGHTING)

法線ベクトルの追加

シェーディングを行うには各面に対して法線ベクトルを指定しなければなりません。

glBegin(GL_TRIANGLES)
glNormal3f(1,1,1)
glVertex3f(0.4,0,0)
glVertex3f(0,0.4,0)
glVertex3f(0,0,0.4)

法線ベクトルの自動正規化

単位法線ベクトルを毎回計算するのは面倒なので、以下のコマンドで自動的に正規化することができます。

glEnable(GL_NORMALIZE)

三角錐と球に光源を追加した例です。

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import*
import sys

class AppBase:
    def __init__(self):
        self.__mouseX = 0
        self.__mouseY = 0
        self.__xrot = 0
        self.__yrot = 0
        
        glutDisplayFunc(self._draw)
        glutMouseFunc(self._mousePressed)
        glutMotionFunc(self._mouseDragged) 
        glutReshapeFunc(self._resize)
        
        glClearColor(0.0,0.0,0.0,0.0)
        self._setLight()
        glEnable(GL_DEPTH_TEST)
        
    def _setLight(self):
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_NORMALIZE) #vector normalization
        ambient = [0.7,0.7,0.9,1.0]
        diffuse = [1.0,1.0,1.0,1.0]
        specular = [1.0,1.0,1.0,1.0]
        
        #glLightfv(GL_LIGHT0,GL_AMBIENT,ambient)
        #glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse)
        #glLightfv(GL_LIGHT0,GL_SPECULAR,specular)                
        
    def _draw(self):        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        gluLookAt(0.0,0.0,5.0,0.0,0.0,-1.0,0.0,1.0,0.0)
        #set position of light0
        position0 =[4.0,0.0,8.0,1.0]
        glLightfv(GL_LIGHT0,GL_POSITION,position0)

        #modeling transform
        glRotatef(self.__xrot,1.0,0.0,0.0)
        glRotatef(self.__yrot,0.0,1.0,0.0)

        #draw x,y,z axis
        glBegin(GL_LINES);
        glColor3f(1.0,1.0,1.0)
        glVertex3f(-1,0,0)
        glVertex3f(1,0,0)
        glVertex3f(0,-1,0)
        glVertex3f(0,1,0)
        glVertex3f(0,0,-1)
        glVertex3f(0,0,1)
        glEnd()
        #draw solid model
        r = 0.4
        glBegin(GL_TRIANGLES)
        glNormal3f(1,1,1)
        glVertex3f(r,0,0)
        glVertex3f(0,r,0)
        glVertex3f(0,0,r)

        glNormal3f(0,0,-1)
        glVertex3f(0,0,0)
        glVertex3f(r,0,0)
        glVertex3f(0,r,0)

        glNormal3f(-1,0,0)
        glVertex3f(0,0,0)
        glVertex3f(0,r,0)
        glVertex3f(0,0,r)

        glNormal3f(0,-1,0)
        glVertex3f(0,0,0)
        glVertex3f(r,0,0)
        glVertex3f(0,0,r)
        glEnd()
        #modeling transform
        glTranslate(0.0,0.0,-0.2)

        glutSolidSphere(r/2,50,50)
        glutSwapBuffers()
        
    def _mousePressed(self,button,state,x,y):
        if(state == GLUT_DOWN):
            glutIdleFunc(self._draw)
        else:
            glutIdleFunc(0)
        self.__mouseX = x
        self.__mouseY = y   
    def _mouseDragged(self,x,y):
        self.__xrot += y - self.__mouseY
        self.__yrot += x - self.__mouseX
        self.__mouseX = x
        self.__mouseY = y
    def _resize(self,width, height):
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(15.0, float(width) / float(height), 3.0, 15.0)
        
        glMatrixMode(GL_MODELVIEW)
def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA  | GLUT_DOUBLE |GLUT_DEPTH)
    glutInitWindowSize(600, 600)
    glutCreateWindow("PyOpenGL")
    
    app = AppBase()
    glutMainLoop()
    
main()

物体の性質の定義

光源から物体に照射され、物質表面で反射した光によって物体の色や性質を知覚することができます。例えば、光源による拡散光によって物体表面の色が定まり、光源がない場合には環境光成分で物体表面の色が決まります。また鏡面反射成分によって物体表面のハイライトが生成されます。これらの光によって定まる物体表面の色を指定するためには、glMaterialfvコマンドを利用して設定したい成分と値を入力します。例えばGL_DIFFUSEは拡散成分による表面色を表現することができます。

mat_diffuse = [0.7,0.7,1.0,1.0]
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse)
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)

上の例に加えて物体に色を付けたものです。(光源をスポットライトにしている)

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import*
import sys

class AppBase:
    def __init__(self):
        self.__mouseX = 0
        self.__mouseY = 0
        self.__xrot = 0
        self.__yrot = 0
        
        glutDisplayFunc(self._draw)
        glutMouseFunc(self._mousePressed)
        glutMotionFunc(self._mouseDragged) 
        glutReshapeFunc(self._resize)
        
        glClearColor(0.0,0.0,0.0,0.0)
        self._setLight()
        glEnable(GL_DEPTH_TEST)
        
    def _setLight(self):
        glEnable(GL_LIGHTING)
        glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE)
        glEnable(GL_LIGHT0)
        glEnable(GL_NORMALIZE)
        ambient = [0.7,0.7,0.9,1.0]
        diffuse = [0.9,0.7,0.9,1.0]
        specular = [0.9,0.9,0.9,1.0]
        
        glLightfv(GL_LIGHT0,GL_AMBIENT,ambient)
        glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse)
        glLightfv(GL_LIGHT0,GL_SPECULAR,specular)
        glLightfv(GL_LIGHT0,GL_CONSTANT_ATTENUATION,1.3)
        glLightfv(GL_LIGHT0,GL_SPOT_CUTOFF,45.0)
        glLightfv(GL_LIGHT0,GL_SPOT_EXPONENT,2.0)
                
        
    def _draw(self):        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        #modeling transform
        glLoadIdentity()
        
        gluLookAt(0.0,0.0,5.0,0.0,0.0,-1.0,0.0,1.0,0.0)
        position0 =[4.0,1.0,8.0,1.0]
        glLightfv(GL_LIGHT0,GL_POSITION,position0)

        glRotatef(self.__xrot,1.0,0.0,0.0)
        glRotatef(self.__yrot,0.0,1.0,0.0)

        #draw x,y,z axis
        glBegin(GL_LINES);
        glColor3f(1.0,1.0,1.0)
        glVertex3f(-1,0,0)
        glVertex3f(1,0,0)
        glVertex3f(0,-1,0)
        glVertex3f(0,1,0)
        glVertex3f(0,0,-1)
        glVertex3f(0,0,1)
        glEnd()
        
        #draw solid model
        r = 0.4
        glBegin(GL_TRIANGLES)
        mat_diffuse = [0.9,0.7,0.7,1.0]
        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse)
        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)
        glNormal3f(1,1,1)
        glVertex3f(r,0,0)
        glVertex3f(0,r,0)
        glVertex3f(0,0,r)

        mat_diffuse = [0.7,0.7,1.0,1.0]
        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse)
        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)
        glNormal3f(0,0,-1)
        glVertex3f(0,0,0)
        glVertex3f(r,0,0)
        glVertex3f(0,r,0)

        mat_diffuse = [0.7,1.0,0.7,1.0]
        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mat_diffuse)
        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)
        glNormal3f(-1,0,0)
        glVertex3f(0,0,0)
        glVertex3f(0,r,0)
        glVertex3f(0,0,r)

        mat_diffuse = [1.0,0.7,0.7,1.0]
        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse)
        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)
        glNormal3f(0,-1,0)
        glVertex3f(0,0,0)
        glVertex3f(r,0,0)
        glVertex3f(0,0,r)
        glEnd()
        
        glTranslate(0.0,0.0,-0.2)
        mat_diffuse = [0.7,0.7,1.0,1.0]
        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse)
        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_diffuse)
        glutSolidSphere(r/2,50,50)
        
        glutSwapBuffers()
        
    def _mousePressed(self,button,state,x,y):
        if(state == GLUT_DOWN):
            glutIdleFunc(self._draw)
        else:
            glutIdleFunc(0)
        self.__mouseX = x
        self.__mouseY = y       
    def _mouseDragged(self,x,y):
        self.__xrot += y - self.__mouseY
        self.__yrot += x - self.__mouseX
        self.__mouseX = x
        self.__mouseY = y   
    def _resize(self,width, height):
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(15.0, float(width) / float(height), 3.0, 15.0)
        
        glMatrixMode(GL_MODELVIEW)
def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA  | GLUT_DOUBLE |GLUT_DEPTH)
    glutInitWindowSize(600, 600)
    glutCreateWindow("PyOpenGL")
    
    app = AppBase()
    glutMainLoop()
    
main()