golangゲーム開発の研究ノート - 自由に3Dの世界を探検作成

書かれたこのテキストgolangゲーム開発の研究ノート-時間をかけてgolang変更色で四角形を描画した後、興味を持って、この記事では、いくつかの基本を知っている記事に行くことができ、我々は非常にシンプルを作成するには、(3つだけキューブ)が、3Dの世界を探検する自由

1.参照

learnOpenGl中国語の翻訳、使用C++実施を。
行く-GL例の行く-GLのサンプル・コードを

2.基本概念

  1. など、関連数学的概念の行列ベクトルなど、インターネット上の関連情報を見つけることができます興味を持っています、
  2. テクスチャは、私たちは地図を作成したモデルとして理解することができます
  3. テクスチャ座標、右上と左下に対応するテクスチャ画像と(1,1)の範囲(0,0)において、
  4. テクスチャサラウンドモードは、テクスチャは、重複画像テクスチャ、色などの他の充填剤として、何をすべきかの範囲を超えて処理後の座標
  5. テクスチャフィルタリング、テクスチャピクセル座標と対応していない、我々は、マッピングモードを指定する必要が
  6. 部分空間、基準点は、オブジェクトを作成するために選択されることが理解されるであろう、他の全ての頂点が基準点に対する物体に配置されている、頂点の座標をローカル座標空間の構成が部分空間と呼ばれていると呼ばれています
  7. 世界の宇宙行列演算の対象は、オブジェクトを作成し、ゲームのシーンとして理解することができ、これはよりよく理解され、後のワールド空間内に移動するオブジェクトになります
  8. スペースの表示名のとおり、世界の視聴者の視点から見た、ワールドスペースで同じ行列演算は、視聴者が見る座標に座標
  9. クリップスペースウィンドウのサイズによって制限されるが、私たちは宇宙の全世界を見ることができ、世界の作物のスペースの必要性は、窓の予約された部分を表示することができます

だから我々は最初のものは、(ローカル座標を担当する3つの行列を、必要localとして名前(オブジェクトを移動するための責任)ワールド座標に)model世界の担当第二は、私たちが名付け、ビューの視聴者の視点から見てきた座標への座標view、私たちは世界の作物の3分の1を見ることができ、名前は、画面上に表示されるprojection変換は、以下を含むことである、頂点座標、

ローカルクリップ=投影*ビュー*モデル*

上記の概念はできlearnOpenGlだけ要約を作るために、コンセプトの詳細な説明を見つけます

3.リライアンス

では行列演算パッケージをサポートしているが、に基づいて、著者を見つけることができませんでしたパッケージ、唯一の名前を見つけるためにパッケージを、ソースコードをよく見てみると、ほとんどすべてのために必要な行列演算は、我々はこの依存ことに注意すべきですパッケージが依存するモジュール、および公式モジュールが強なので、最高のは、手動のディレクトリを作成するディレクトリをしてからインストールしたディレクトリに保存された画像に直接依存して、ダウンロードの依存を実行した後、C++openglGLM(OpenGL Mathematics)golangGLMmglimageimagegopathgolang.org\xgithubimage
go get github.com/go-gl/mathgl/

4.実装

1.負荷テクスチャテクスチャ用のクラスを作成します。

package texture
import(
	"os"
	"image"
	"image/jpeg"
	"errors"
	"github.com/go-gl/gl/v4.1-core/gl"
	"image/draw"
)

type LocalTexture struct{
	ID uint32
	TEXTUREINDEX uint32
}

func NewLocalTexture(file string, TEXTUREINDEX uint32) *LocalTexture{
	imgFile, err := os.Open(file)
	if err != nil {
		panic(err)
	}

	img, err := jpeg.Decode(imgFile)
	if err != nil {
		panic(err)
	}
	rgba := image.NewRGBA(img.Bounds())
	if rgba.Stride != rgba.Rect.Size().X*4 {
		panic(errors.New("unsupported stride"))
	}
	draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
	
	var textureID uint32
	gl.GenTextures(1, &textureID)
	gl.ActiveTexture(TEXTUREINDEX)
	gl.BindTexture(gl.TEXTURE_2D, textureID)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		gl.RGBA,
		int32(rgba.Rect.Size().X),
		int32(rgba.Rect.Size().Y),
		0,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		gl.Ptr(rgba.Pix))
	return &LocalTexture{ ID: textureID,TEXTUREINDEX:TEXTUREINDEX}
}

func (texture *LocalTexture) Use(){
	gl.ActiveTexture(texture.TEXTUREINDEX)
	gl.BindTexture(gl.TEXTUR_2D, texture.ID)
}

このコンストラクタでは、(ここでは簡略化のため、唯一の分析JPG形式の異なるファイル形式を解析することができ、画像/映像のパッケージ形式でデコードメソッドを呼び出す)ファイルパスのテクスチャJPG形式に合格する必要があり、型UINT32のパラメータ私たちが使用している場合、パラメータは、複数のテクスチャ、テクスチャシェーダプログラム異なるプレースホルダを指定します。コンストラクタでは、フィルタの周りにテクスチャフィルタリングとテクスチャズームモードを指定します

2.変換行列を作成します

私たちは、オブジェクトの座標を変換3つの行列を使用する必要があり、前述の

model = mgl32.Translate3D(0,0,1)

この行列は、ユニットが、Z軸方向に沿ってオブジェクトを移動し
、次に呼び出す二つの平面の間の唯一の物体、遠い面が100に設定され、周辺の平面が0.1である、45に設定斜視マトリックス、フィールドサイズを作成する方法をレンダリングされ、それが位置まで離れて不可視のオブジェクトとして理解することができますmglPerspective

projection := mgl32.Perspective(45, width/height, 0.1, 100.0)

最後に、我々が使用するオブジェクトに座標観測者の私たちの視点を調整するために使用されるビュー行列を作成する方法を、この方法は、ワールド空間における観察者の位置である第一そのうち3つのベクトルを、必要とし、第二は、直接最後の上方に、観察する観察者の方向(右フォークベクターを通じてしたベクトルと観察方向とすることができる)、視聴者の視線方向を表すベクトルでありますmglLookAt

position := mgl32.Vec3{0, 0, 0}
front := mgl32.Vec3{0, 0, -1}
up:= mgl32.Vec3{0, 1, 0}

target := position.Add(front)
view := mgl32.LookAtV(position,target, up)

コードposition(原点として)配置され、観察者の位置を表す、front、観察者が見た(z軸負方向に)代表的な方向up、垂直方向の代わりに。
すべての操作はモデル行列を変更することによって達成することができ、移動物体に、これら三つの行列演算に変換された後、この時点で、私たちは、必要なすべての空間の3Dマトリックスを作成する準備ができました、視点を変更する必要がある唯一のビュー行列を変更する必要があり、ズーム責任ビジョンは、投影行列を修正することによって達成することができます。
一つの問題は、私たちが移動し、変更するには、マウスで観察方向をどのように作るのですか?、フロントノウハウfront観測観測者に代わって、このベクトルの修正はオイラー角に、特定の情報を見つけるためにオンラインで行くことができ、ここで使用されているが、私は本当に理解していない、ここで直接コードのチュートリアルをコピーして
、関連するさらなる抽象への最後の操作、カメラのクラスを作成します

package camera
import(
	"github.com/go-gl/mathgl/mgl64"
	"github.com/go-gl/mathgl/mgl32"
	"math"
)
type Direction int
const (
    FORWARD   Direction = 0 	// 摄像机移动状态:前
    BACKWARD  Direction = 1     // 后
    LEFT      Direction = 2     // 左
    RIGHT     Direction = 3     // 右
)
type LocalCamera struct{
	position    mgl32.Vec3
	front       mgl32.Vec3
	up	        mgl32.Vec3
	right		mgl32.Vec3

	wordUp      mgl32.Vec3

	yaw   float64
	pitch float64
	zoom  float32
	
	movementSpeed float32
	mouseSensitivity float32

	constrainPitch bool

}
func NewDefaultCamera() *LocalCamera{
	position := mgl32.Vec3{0, 0, 0}
	front := mgl32.Vec3{0, 0, -1}
	wordUp := mgl32.Vec3{0, 1, 0}
	yaw := float64(-90)
	pitch := float64(0)
	movementSpeed := float32(2.5)
	mouseSensitivity := float32(0.1)
	zoom := float32(45)
	constrainPitch := true
	localCamera := &LocalCamera{position:position, 
								front:front, 
								wordUp:wordUp, 
								yaw:yaw, 
								pitch:pitch, 
								movementSpeed:movementSpeed, 
								mouseSensitivity:mouseSensitivity, 
								zoom:zoom,
								constrainPitch:constrainPitch}
	localCamera.updateCameraVectors()
	return localCamera
}
//获取当前透视矩阵
func (localCamera *LocalCamera) GetProjection(width float32, height float32) *float32{
	projection := mgl32.Perspective(mgl32.DegToRad(localCamera.zoom), float32(width)/height, 0.1, 100.0)
	return &projection[0]
}
//鼠标移动回调
func (localCamera *LocalCamera) ProcessMouseMovement(xoffset float32, yoffset float32){
	xoffset *= localCamera.mouseSensitivity
	yoffset *= localCamera.mouseSensitivity

	localCamera.yaw += float64(xoffset)
	localCamera.pitch += float64(yoffset)

	// Make sure that when pitch is out of bounds, screen doesn't get flipped
	if (localCamera.constrainPitch){
		if (localCamera.pitch > 89.0){
			localCamera.pitch = 89.0
		}
		if (localCamera.pitch < -89.0){
			localCamera.pitch = -89.0
		}
	}
	localCamera.updateCameraVectors();
}
//鼠标滑动回调
func (localCamera *LocalCamera) ProcessMouseScroll(yoffset float32){
	if (localCamera.zoom >= 1.0 && localCamera.zoom <= 45.0){
		localCamera.zoom -= yoffset;
	}
	if (localCamera.zoom <= 1.0){
		localCamera.zoom = 1.0;
	}
	if (localCamera.zoom >= 45.0){
		localCamera.zoom = 45.0;
	}
}
//键盘回调
func (localCamera *LocalCamera) ProcessKeyboard(direction Direction, deltaTime float32){
	velocity := localCamera.movementSpeed * deltaTime;
	if (direction == FORWARD){
		localCamera.position = localCamera.position.Add(localCamera.front.Mul(velocity))
	}
	if (direction == BACKWARD){
		localCamera.position = localCamera.position.Sub(localCamera.front.Mul(velocity))
	}
	if (direction == LEFT){
		localCamera.position = localCamera.position.Sub(localCamera.right.Mul(velocity))
	}
	if (direction == RIGHT){
		localCamera.position = localCamera.position.Add(localCamera.right.Mul(velocity))
	}
}
//获取view
func (localCamera *LocalCamera) GetViewMatrix() *float32{
	target := localCamera.position.Add(localCamera.front)
	view := mgl32.LookAtV(localCamera.position,target, localCamera.up)
	return &view[0]
}
//更新view
func (localCamera *LocalCamera) updateCameraVectors(){
	x := math.Cos(mgl64.DegToRad(localCamera.yaw)) * math.Cos(mgl64.DegToRad(localCamera.pitch))
	y := math.Sin(mgl64.DegToRad(localCamera.pitch))
	z := math.Sin(mgl64.DegToRad(localCamera.yaw)) * math.Cos(mgl64.DegToRad(localCamera.pitch));
	localCamera.front = mgl32.Vec3{float32(x),float32(y),float32(z)}

	localCamera.right = localCamera.front.Cross(localCamera.wordUp).Normalize()
	localCamera.up = localCamera.right.Cross(localCamera.front).Normalize()
}

3.シェーダーを作成します。

私たちはシェーダークラスとしてパッケージ化されるすべてのプロセスを、作成するシェーダを書いた前回の記事では、ファイルから直接構築され、シェーダをコンパイルすることができます

package shader

import (
	"io/ioutil"
	"fmt"
	"github.com/go-gl/gl/v4.1-core/gl"
	"strings"
)

type LocalShader struct{
	ID uint32
}

func (shader *LocalShader) Use(){
	gl.UseProgram(shader.ID)
}

func (shader *LocalShader) SetBool(name string, value bool){
	var a int32 = 0;
	if(value){
		a = 1
	}
	gl.Uniform1i(gl.GetUniformLocation(shader.ID, gl.Str(name + "\x00")), a)
}

func (shader *LocalShader) SetInt(name string, value int32){
	gl.Uniform1i(gl.GetUniformLocation(shader.ID, gl.Str(name + "\x00")), value)
}

func (shader *LocalShader) SetFloat(name string, value float32){
	gl.Uniform1f(gl.GetUniformLocation(shader.ID, gl.Str(name + "\x00")), value)
}

func (shader *LocalShader) SetMatrix4fv(name string, value *float32){
	gl.UniformMatrix4fv(gl.GetUniformLocation(shader.ID, gl.Str(name + "\x00")), 1,false,value)
}

func NewLocalShader(vertexPath string, fragmentPath string) *LocalShader{
	vertexString, err := ioutil.ReadFile(vertexPath)
	if err != nil{
        panic(err)
	}
	fragmentString, err := ioutil.ReadFile(fragmentPath)
	if err != nil{
        panic(err)
	}

	return NewStringShader(string(vertexString),string(fragmentString))
}

func NewStringShader(vertexString string, fragmentString string) *LocalShader{
	vertexShader,err := compileShader(vertexString+"\x00", gl.VERTEX_SHADER)
	if err != nil{
        panic(err)
	}
	fragmentShader,err := compileShader(fragmentString+"\x00", gl.FRAGMENT_SHADER)
	if err != nil{
        panic(err)
	}

	progID := gl.CreateProgram()
	gl.AttachShader(progID, vertexShader)
    gl.AttachShader(progID, fragmentShader)    
	gl.LinkProgram(progID)
	gl.DeleteShader(vertexShader)
	gl.DeleteShader(fragmentShader)
	return &LocalShader{ ID: progID}
}

func compileShader(source string, shaderType uint32) (uint32, error) {
    shader := gl.CreateShader(shaderType)
    csources, free := gl.Strs(source)
    gl.ShaderSource(shader, 1, csources, nil)
    free()
	gl.CompileShader(shader)
	
    var status int32
    gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
    if status == gl.FALSE {
        var logLength int32
        gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
        log := strings.Repeat("\x00", int(logLength+1))
        gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
        return 0, fmt.Errorf("failed to compile %v: %v", source, log)
    }
    return shader, nil
}

次の2つのシェーダコード

#version 410 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

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

void main(){
    gl_Position = projection * view * model * vec4(aPos,1.0);
    TexCoord = aTexCoord;
}
#version 410 core
out vec4 FragColor;

in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5);
}

4.統合

我々はmain、キー入力、マウス移動処理を含む処理を、上記方法の統合を行います

package main

import(
    "github.com/go-gl/glfw/v3.2/glfw"
    "github.com/go-gl/gl/v4.1-core/gl"
    "log"
    "legend/shader"
    "runtime"
    "legend/texture"
    "legend/camera"
    "github.com/go-gl/mathgl/mgl32"
)
const (
    width  = 800
    height = 600
)
var (
    vertices = []float32 {
       -0.5, -0.5, -0.5,  0.0, 0.0,
        0.5, -0.5, -0.5,  1.0, 0.0,
        0.5,  0.5, -0.5,  1.0, 1.0,
        0.5,  0.5, -0.5,  1.0, 1.0,
       -0.5,  0.5, -0.5,  0.0, 1.0,
       -0.5, -0.5, -0.5,  0.0, 0.0,
   
       -0.5, -0.5,  0.5,  0.0, 0.0,
        0.5, -0.5,  0.5,  1.0, 0.0,
        0.5,  0.5,  0.5,  1.0, 1.0,
        0.5,  0.5,  0.5,  1.0, 1.0,
       -0.5,  0.5,  0.5,  0.0, 1.0,
       -0.5, -0.5,  0.5,  0.0, 0.0,
   
       -0.5,  0.5,  0.5,  1.0, 0.0,
       -0.5,  0.5, -0.5,  1.0, 1.0,
       -0.5, -0.5, -0.5,  0.0, 1.0,
       -0.5, -0.5, -0.5,  0.0, 1.0,
       -0.5, -0.5,  0.5,  0.0, 0.0,
       -0.5,  0.5,  0.5,  1.0, 0.0,
   
        0.5,  0.5,  0.5,  1.0, 0.0,
        0.5,  0.5, -0.5,  1.0, 1.0,
        0.5, -0.5, -0.5,  0.0, 1.0,
        0.5, -0.5, -0.5,  0.0, 1.0,
        0.5, -0.5,  0.5,  0.0, 0.0,
        0.5,  0.5,  0.5,  1.0, 0.0,
   
       -0.5, -0.5, -0.5,  0.0, 1.0,
        0.5, -0.5, -0.5,  1.0, 1.0,
        0.5, -0.5,  0.5,  1.0, 0.0,
        0.5, -0.5,  0.5,  1.0, 0.0,
       -0.5, -0.5,  0.5,  0.0, 0.0,
       -0.5, -0.5, -0.5,  0.0, 1.0,
   
       -0.5,  0.5, -0.5,  0.0, 1.0,
        0.5,  0.5, -0.5,  1.0, 1.0,
        0.5,  0.5,  0.5,  1.0, 0.0,
        0.5,  0.5,  0.5,  1.0, 0.0,
       -0.5,  0.5,  0.5,  0.0, 0.0,
       -0.5,  0.5, -0.5,  0.0, 1.0,
    };
    position = []mgl32.Mat3{
        mgl32.Mat3{0,0,0},
        mgl32.Mat3{2,5,-15}, 
        mgl32.Mat3{-1.5,-2.2,-2.5}, 
    }
    deltaTime = float32(0.0);	// time between current frame and last frame
    lastFrame = float32(0.0);
    acamera = camera.NewDefaultCamera()
    firstMouse = true
    lastX = width / 2.0
    lastY = height / 2.0
)
func main() {
    runtime.LockOSThread()
    window := initGlfw()
    defer glfw.Terminate()
    initOpenGL()
    vao,vbo := makeVao(vertices,nil)

    shader := shader.NewLocalShader("./shader/shader-file/shader.vs","./shader/shader-file/shader.fs")
    shader.Use()
    shader.SetInt("texture1", 0)
    shader.SetInt("texture2", 1)

    texture1 := texture.NewLocalTexture("./texture/texture-file/face.jpg",gl.TEXTURE0)
    texture2 := texture.NewLocalTexture("./texture/texture-file/wood.jpg",gl.TEXTURE1)
    texture1.Use()
    texture2.Use()

    projection := acamera.GetProjection(width,height)
    shader.SetMatrix4fv("projection", projection)
    for !window.ShouldClose() {
        currentFrame := float32(glfw.GetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        clear()
        texture1.Use()
        texture2.Use()
        view := acamera.GetViewMatrix()
        shader.SetMatrix4fv("view",view)
        for _, v := range position {
            model := mgl32.HomogRotate3DX(float32(glfw.GetTime())).Mul4(mgl32.HomogRotate3DY(float32(glfw.GetTime())))
            model = mgl32.Translate3D(v[0],v[1],v[2]).Mul4(model)
            shader.SetMatrix4fv("model",&model[0])
            draw(vao)
        }
        processInput(window)
        glfw.PollEvents()
        window.SwapBuffers()
    }
    gl.DeleteVertexArrays(1, &vao);
    gl.DeleteBuffers(1, &vbo);
    glfw.Terminate()
}
func initGlfw() *glfw.Window {
    if err := glfw.Init(); err != nil {
            panic(err)
    }
    glfw.WindowHint(glfw.Resizable, glfw.False)
    window, err := glfw.CreateWindow(width, height, "test", nil, nil)
    window.SetCursorPosCallback(mouse_callback)
    if err != nil {
            panic(err)
    }
    window.MakeContextCurrent()
    window.SetInputMode(glfw.CursorMode,glfw.CursorDisabled)
    return window
}
func initOpenGL(){
    if err := gl.Init(); err != nil {
            panic(err)
    }
    version := gl.GoStr(gl.GetString(gl.VERSION))
    log.Println("OpenGL version", version)
    gl.Enable(gl.DEPTH_TEST)
}

func makeVao(points []float32,indices []uint32) (uint32,uint32) {
    var vbo uint32
    gl.GenBuffers(1, &vbo)
    gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
    gl.BufferData(gl.ARRAY_BUFFER,4*len(points), gl.Ptr(points), gl.STATIC_DRAW)

    var vao uint32
    gl.GenVertexArrays(1, &vao)
    gl.BindVertexArray(vao)
    
    gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 5 * 4, gl.PtrOffset(0))
    gl.EnableVertexAttribArray(0)
    gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 5 * 4, gl.PtrOffset(3 * 4))
    gl.EnableVertexAttribArray(1)

    if(indices != nil){
        var ebo uint32
        gl.GenBuffers(2,&ebo)
        gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER,ebo)
        gl.BufferData(gl.ELEMENT_ARRAY_BUFFER,4*len(indices),gl.Ptr(indices),gl.STATIC_DRAW)

    }
    return vao,vbo
}

func processInput(window *glfw.Window){
    if(window.GetKey(glfw.KeyW) == glfw.Press){
        acamera.ProcessKeyboard(camera.FORWARD,deltaTime)
    }
    if(window.GetKey(glfw.KeyS) == glfw.Press){
        acamera.ProcessKeyboard(camera.BACKWARD,deltaTime)
    }
    if(window.GetKey(glfw.KeyA) == glfw.Press){
        acamera.ProcessKeyboard(camera.LEFT,deltaTime)
    }
    if(window.GetKey(glfw.KeyD) == glfw.Press){
        acamera.ProcessKeyboard(camera.RIGHT,deltaTime)
    }
    if(window.GetKey(glfw.KeyEscape) == glfw.Press){
        window.SetShouldClose(true)
    } 
}

func mouse_callback(window *glfw.Window, xpos float64, ypos float64){
    if(firstMouse){
        lastX = xpos
        lastY = ypos
        firstMouse = false
    }
    xoffset := float32(xpos - lastX)
    yoffset := float32(lastY - ypos) 

    lastX = xpos
    lastY = ypos

    acamera.ProcessMouseMovement(xoffset, yoffset)
}

func draw(vao uint32) {
    gl.BindVertexArray(vao)
    gl.DrawArrays(gl.TRIANGLES,0,36)
}
func clear(){
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 
}

5.結果

ここに画像を挿入説明

おすすめ

転載: blog.csdn.net/qq_35488769/article/details/94431818