Пример #1
0
//Aggregate performs the lighting calculation per pixel. This is essentially a special post process pass.
func (gb *GBuffer) Aggregate(cam *Camera, plights []*PointLight, shadowmat glm.Mat4, tex gl2.Texture2D, f1, f2, f3 float32) {
	gb.AggregateFramebuffer.framebuffer.Bind(gl2.FRAMEBUFFER)

	gb.AggregateFramebuffer.program.Use()

	gb.CookRoughnessValue.Uniform1f(f1)
	gb.CookF0.Uniform1f(f2)
	gb.CookK.Uniform1f(f3)

	gl.ActiveTexture(gl2.TEXTURE0)
	gb.DiffuseTex.Bind()
	gb.AggregateFramebuffer.DiffUni.Uniform1i(0)

	gl.ActiveTexture(gl2.TEXTURE1)
	gb.NormalTex.Bind()
	gb.AggregateFramebuffer.NormalUni.Uniform1i(1)

	gl.ActiveTexture(gl2.TEXTURE2)
	gb.PositionTex.Bind()
	gb.AggregateFramebuffer.PosUni.Uniform1i(2)

	gl.ActiveTexture(gl2.TEXTURE3)
	gb.DepthTex.Bind()
	gb.AggregateFramebuffer.DepthUni.Uniform1i(3)

	//point lights
	gb.NumPointLightUni.Uniform1i(int32(len(plights)))
	plightpos := make([]float32, len(plights)*3)
	plightcol := make([]float32, len(plights)*3)
	for i, light := range plights {
		plightpos[i] = light.X
		plightpos[i+1] = light.Y
		plightpos[i+2] = light.Z

		plightcol[i] = light.R
		plightcol[i] = light.G
		plightcol[i] = light.B
	}
	if len(plights) != 0 {
		gb.PointLightPosUni.Uniform3fv(int32(len(plights)), &plightpos[0])
		gb.PointLightColUni.Uniform3fv(int32(len(plights)), &plightcol[0])
	}
	gb.CamPosUni.Uniform3fv(1, &cam.Pos[0])

	//=====shadow=====//
	gl.ActiveTexture(gl2.TEXTURE4)
	tex.Bind()
	gb.ShadowMapUni.Uniform1i(4)

	gb.ShadowMatUni.UniformMatrix4fv(1, false, &shadowmat[0])
	//================//

	Fstri()
}
Пример #2
0
func LoadText(f *Font) (t *Text) {
	t = new(Text)
	t.font = f

	// text hover values
	// "resting state" of a text object is the min scale
	t.ScaleMin = 1.0
	t.ScaleMax = 1.1
	t.SetScale(1)

	// size of glfloat
	glfloat_size := int32(4)

	// stride of the buffered data
	xy_count := int32(2)
	stride := xy_count + int32(2)

	gl.GenVertexArrays(1, &t.vao)
	gl.GenBuffers(1, &t.vbo)
	gl.GenBuffers(1, &t.ebo)

	// vao
	gl.BindVertexArray(t.vao)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, t.font.textureID)

	// vbo
	// specify the buffer for which the VertexAttribPointer calls apply
	gl.BindBuffer(gl.ARRAY_BUFFER, t.vbo)

	gl.EnableVertexAttribArray(t.font.centeredPosition)
	gl.VertexAttribPointer(
		t.font.centeredPosition,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(0),
	)

	gl.EnableVertexAttribArray(t.font.uv)
	gl.VertexAttribPointer(
		t.font.uv,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(int(glfloat_size*xy_count)),
	)

	// ebo
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, t.ebo)

	// i am guessing that order is important here
	gl.BindVertexArray(0)
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	return t
}
Пример #3
0
func (tr *TextRenderer) Draw(tex *Texture, x, y, scale float32) (err error) {
	gl.ActiveTexture(gl.TEXTURE0)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	gl.BindTexture(gl.TEXTURE_2D, tex.Texture)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	gl.Uniform3f(tr.ScaleLoc, float32(tex.Width)*scale, float32(tex.Height)*scale, 1)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	gl.Uniform3f(tr.TransLoc, x, y, 0)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	if e := gl.GetError(); e != 0 {
		return fmt.Errorf("ERROR: %X", e)
	}
	return nil
}
Пример #4
0
//LoadPng tries to load a png file from hard drive and upload it to the GPU.
func LoadPng(file string) (gl2.Texture2D, error) {
	imgFile, err := os.Open(file)
	if err != nil {
		return 0, nil
	}
	defer imgFile.Close()
	img, _, err := image.Decode(imgFile)
	if err != nil {
		return 0, nil
	}

	rgba := image.NewRGBA(img.Bounds())
	if rgba.Stride != rgba.Rect.Size().X*4 {
		return 0, fmt.Errorf("unsupported stride")
	}
	draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)

	texture := gl2.GenTexture2D()
	gl.ActiveTexture(gl2.TEXTURE0)
	texture.Bind()
	defer texture.Unbind()
	texture.MinFilter(gl2.LINEAR)
	texture.MagFilter(gl2.LINEAR)
	texture.WrapS(gl2.CLAMP_TO_EDGE)
	texture.WrapT(gl2.CLAMP_TO_EDGE)
	texture.TexImage2D(0, gl2.RGBA, int32(rgba.Rect.Size().X), int32(rgba.Rect.Size().Y), 0, gl2.RGBA, gl2.UNSIGNED_BYTE, gl.Ptr(rgba.Pix))

	return texture, nil
}
Пример #5
0
func (r *GlowRenderer) Draw() (err error) {
	gl.UseProgram(r.shader)
	gl.Uniform1i(r.textureUnitLoc, 0)
	gl.BindBuffer(gl.ARRAY_BUFFER, r.coords)
	gl.EnableVertexAttribArray(r.positionLoc)
	gl.VertexAttribPointer(r.positionLoc, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0))
	gl.EnableVertexAttribArray(r.textureLoc)
	gl.VertexAttribPointer(r.textureLoc, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4))
	gl.Uniform1i(r.blurAmountLoc, int32(r.blur))
	gl.Uniform1f(r.blurScaleLoc, r.scale)
	gl.Uniform1f(r.blurStrengthLoc, r.strength)
	gl.Uniform2f(r.bufferDimensionsLoc, float32(r.width), float32(r.height))

	gl.BindFramebuffer(gl.FRAMEBUFFER, r.BlurFb)
	gl.Viewport(0, 0, int32(r.width), int32(r.height))
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, r.GlowTex)
	gl.Uniform1i(r.orientationLoc, 0)
	gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
	gl.BindFramebuffer(gl.FRAMEBUFFER, 0)

	gl.Viewport(0, 0, int32(r.oldwidth), int32(r.oldheight))
	gl.BlendFunc(gl.ONE, gl.ONE)
	gl.BindTexture(gl.TEXTURE_2D, r.BlurTex)
	gl.Uniform1i(r.orientationLoc, 1)
	gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)

	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	return nil
}
Пример #6
0
func (r *BatchRenderer) Bind() error {
	gl.UseProgram(r.Program)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.Uniform1i(r.TextureUnitLoc, 0)
	gl.UniformMatrix4fv(r.ProjectionLoc, 1, false, &r.Camera.Projection[0])
	return nil
}
Пример #7
0
func (t *Text) Draw() {
	if t.IsDebug {
		t.BoundingBox.Draw()
	}
	gl.UseProgram(t.font.program)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, t.font.textureID)

	// uniforms
	gl.Uniform1i(t.font.fragmentTextureUniform, 0)
	gl.Uniform4fv(t.font.colorUniform, 1, &t.color[0])
	gl.Uniform2fv(t.font.finalPositionUniform, 1, &t.finalPosition[0])
	gl.UniformMatrix4fv(t.font.orthographicMatrixUniform, 1, false, &t.font.OrthographicMatrix[0])
	gl.UniformMatrix4fv(t.font.scaleMatrixUniform, 1, false, &t.scaleMatrix[0])

	// draw
	drawCount := int32(t.RuneCount * 6)
	if drawCount > int32(t.eboIndexCount) {
		drawCount = int32(t.eboIndexCount)
	}
	if drawCount < 0 {
		drawCount = 0
	}
	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.BindVertexArray(t.vao)
	gl.DrawElements(gl.TRIANGLES, drawCount, gl.UNSIGNED_INT, nil)
	gl.BindVertexArray(0)
	gl.Disable(gl.BLEND)
}
Пример #8
0
func newTexture(file string, texNum uint32) (uint32, error) {
	imgFile, err := os.Open(file)
	if err != nil {
		return 0, err
	}
	img, _, err := image.Decode(imgFile)

	rgba := image.NewRGBA(img.Bounds())
	if rgba.Stride != rgba.Rect.Size().X*4 {
		return 0, fmt.Errorf("unsupported stride")
	}
	draw.Draw(rgba, rgba.Bounds(), img, image.ZP, draw.Src)

	var texture uint32
	gl.GenTextures(1, &texture)
	gl.ActiveTexture(texNum)
	gl.BindTexture(gl.TEXTURE_2D, texture)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	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 texture, nil
}
Пример #9
0
func (texture Texture) Bind(textureSlot int) {
	if textureSlot > int(gl.TEXTURE31) {
		tlog.Panic("active texture slot too large: ", textureSlot)
	}
	gl.ActiveTexture(uint32(gl.TEXTURE0 + textureSlot))
	Check()
	gl.BindTexture(gl.TEXTURE_2D, uint32(texture))
	Check()
}
Пример #10
0
//Render takes a texture and feed it to the fragment shader as a fullscreen texture. It will call the next post process pass if there is one.
func (ppfb *PostProcessFramebuffer) Render(t gl2.Texture2D) {
	ppfb.Prog.Use()
	ppfb.time.Uniform1f(float32(glfw.GetTime()))

	gl.ActiveTexture(gl2.TEXTURE0)
	t.Bind()
	ppfb.source.Uniform1i(0)
	Fstri()
	if ppfb.next != nil {
		ppfb.next.PreRender()
		ppfb.next.Render(ppfb.Tex)
	}
}
Пример #11
0
func (renderObject *RenderObject) Render() {
	gl.BindVertexArray(renderObject.vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, renderObject.vbo)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderObject.ebo)
	gl.UseProgram(renderObject.shaderProgram)

	// Binding the texture.
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, renderObject.texture)
	gl.BindFragDataLocation(renderObject.shaderProgram, 0, gl.Str("outputColor\x00"))

	// Drawing the object.
	gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
}
Пример #12
0
func createTexture(textureUnit uint32, rgba *image.RGBA) (uint32, error) {
	var texture uint32
	gl.GenTextures(1, &texture)
	gl.ActiveTexture(textureUnit)
	gl.BindTexture(gl.TEXTURE_2D, texture)

	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.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

	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 texture, nil
}
Пример #13
0
//Render will render the mesh in the different textures. No lighting calculation is performed here.
func (gb *GBuffer) Render(cam *Camera, mesh Mesh, tex gl2.Texture2D, t *Transform) {

	model := t.Mat4()
	mvp := gb.vp.Mul4(model)
	gb.MVPUni.UniformMatrix4fv(1, false, &mvp[0])

	gb.MUni.UniformMatrix4fv(1, false, &model[0])

	normal := model.Inv()
	gb.NUni.UniformMatrix4fv(1, true, &normal[0])

	gl.ActiveTexture(gl2.TEXTURE0)
	tex.Bind()
	gb.DiffuseUni.Uniform1i(0)

	mesh.Bind()
	mesh.DrawCall()
}
Пример #14
0
// Attempting to load a Texture from a given location on the disk.
func LoadTexture(path string) (Texture, error) {
	// Loading the image data.
	imgFile, err := os.Open(path)
	if err != nil {
		return 0, err
	}

	img, _, err := image.Decode(imgFile)
	if err != nil {
		return 0, err
	}

	rgba := image.NewRGBA(img.Bounds())
	if rgba.Stride != rgba.Rect.Size().X*4 {
		return 0, errors.New("Unsupported stride.")
	}
	draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)

	// Generating and populating the texture.
	var texture uint32

	gl.GenTextures(1, &texture)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, texture)

	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.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

	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 Texture(texture), nil
}
Пример #15
0
// RenderString must be called on the render thread.  x and y are the initial position of the pen,
// in screen coordinates, and height is the height of a full line of text, in screen coordinates.
func (d *Dictionary) RenderString(str string, x, y, height float64) {
	if str == "" {
		return
	}
	// No synchronization necessary because everything is run serially on the render thread anyway.
	if d.strs == nil {
		d.strs = make(map[string]strData)
	}
	data, ok := d.strs[str]
	if !ok {
		data = d.bindString(str)
		d.strs[str] = data
	}

	render.EnableShader("glop.font")
	defer render.EnableShader("")

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, d.atlas.texture)
	location, _ := render.GetUniformLocation("glop.font", "tex")
	gl.Uniform1i(location, 0)
	gl.BindSampler(0, d.atlas.sampler)

	location, _ = render.GetUniformLocation("glop.font", "height")
	gl.Uniform1f(location, float32(height))

	var viewport [4]int32
	gl.GetIntegerv(gl.VIEWPORT, &viewport[0])
	location, _ = render.GetUniformLocation("glop.font", "screen")
	gl.Uniform2f(location, float32(viewport[2]), float32(viewport[3]))

	location, _ = render.GetUniformLocation("glop.font", "pen")
	gl.Uniform2f(location, float32(x)+float32(viewport[0]), float32(y)+float32(viewport[1]))

	location, _ = render.GetUniformLocation("glop.font", "textColor")
	gl.Uniform3f(location, d.color[0], d.color[1], d.color[2])

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.BindVertexArray(data.varrays[0])
	gl.DrawArrays(gl.TRIANGLES, 0, data.count)
}
Пример #16
0
func LoadImageToTexture(filePath string) (glTex uint32, e error) {
	gl.GenTextures(1, &glTex)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, glTex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR_MIPMAP_LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)

	rgba_flipped, err := loadFile(filePath)
	if err != nil {
		return glTex, err
	}

	imageSize := int32(rgba_flipped.Bounds().Max.X)

	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, imageSize, imageSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba_flipped.Pix))
	gl.GenerateMipmap(gl.TEXTURE_2D)
	return glTex, nil
}
Пример #17
0
func CreateTexture(file string) (Texture, error) {
	imgFile, err := os.Open(file)
	if err != nil {
		return Texture{}, err
	}
	img, _, err := image.Decode(imgFile)
	if err != nil {
		return Texture{}, err
	}

	rgba := image.NewRGBA(img.Bounds())
	if rgba.Stride != rgba.Rect.Size().X*4 {
		return Texture{}, fmt.Errorf("unsupported stride")
	}
	draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
	width, height := int32(rgba.Rect.Size().X), int32(rgba.Rect.Size().Y)
	var texture uint32

	gl.GenTextures(1, &texture)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, texture)
	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.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		gl.RGBA,
		width,
		height,
		0,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		gl.Ptr(rgba.Pix))

	return Texture{Id: texture, Width: width, Height: height}, nil
}
Пример #18
0
func bindAggregateImage(img image.Image, idx int) uint32 {

	newIdx := idx

	if rgba, ok := img.(*image.RGBA); ok {

		gl.GenTextures(1, &textures[newIdx])

		gl.ActiveTexture(gl.TEXTURE0 + uint32(newIdx))
		gl.BindTexture(gl.TEXTURE_2D, textures[newIdx])
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)

		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))
		gl.GenerateMipmap(gl.TEXTURE_2D)

		idxS := strconv.Itoa(newIdx)
		fmt.Println("idx: ", int32(newIdx), "myTextureSampler["+idxS+"]\x00")
		gl.Uniform1i(gl.GetUniformLocation(program, gl.Str("myTextureSampler["+idxS+"]\x00")), int32(newIdx))
		if ok := gl.GetError(); ok != gl.NO_ERROR {

			fmt.Println("1- Cannot load Image in location: ./: ", ok)
			os.Exit(-1)
		}

		return textures[newIdx]
	} else {
		fmt.Println("Image not RGBA at location: ./")
		os.Exit(-1)
	}

	return 0
}
Пример #19
0
func (r *Renderable) Draw(perspective mgl.Mat4, view mgl.Mat4) {
	gl.UseProgram(r.Shader)
	gl.BindVertexArray(r.Vao)

	model := r.GetTransformMat4()

	var mvp mgl.Mat4
	shaderMvp := getUniformLocation(r.Shader, "MVP_MATRIX")
	if shaderMvp >= 0 {
		mvp = perspective.Mul4(view).Mul4(model)
		gl.UniformMatrix4fv(shaderMvp, 1, false, &mvp[0])
	}

	shaderMv := getUniformLocation(r.Shader, "MV_MATRIX")
	if shaderMv >= 0 {
		mv := view.Mul4(model)
		gl.UniformMatrix4fv(shaderMv, 1, false, &mv[0])
	}

	shaderTex0 := getUniformLocation(r.Shader, "DIFFUSE_TEX")
	if shaderTex0 >= 0 {
		gl.ActiveTexture(gl.TEXTURE0)
		gl.BindTexture(gl.TEXTURE_2D, r.Tex0)
		gl.Uniform1i(shaderTex0, 0)
	}

	shaderColor := getUniformLocation(r.Shader, "MATERIAL_DIFFUSE")
	if shaderColor >= 0 {
		gl.Uniform4f(shaderColor, r.Color[0], r.Color[1], r.Color[2], r.Color[3])
	}

	shaderTex1 := getUniformLocation(r.Shader, "MATERIAL_TEX_0")
	if shaderTex1 >= 0 {
		gl.ActiveTexture(gl.TEXTURE0)
		gl.BindTexture(gl.TEXTURE_2D, r.Tex0)
		gl.Uniform1i(shaderTex1, 0)
	}

	shaderCameraWorldPos := getUniformLocation(r.Shader, "CAMERA_WORLD_POSITION")
	if shaderCameraWorldPos >= 0 {
		gl.Uniform3f(shaderCameraWorldPos, -view[12], -view[13], -view[14])
	}

	shaderPosition := getAttribLocation(r.Shader, "VERTEX_POSITION")
	if shaderPosition >= 0 {
		gl.BindBuffer(gl.ARRAY_BUFFER, r.VertVBO)
		gl.EnableVertexAttribArray(uint32(shaderPosition))
		gl.VertexAttribPointer(uint32(shaderPosition), 3, gl.FLOAT, false, 0, gl.PtrOffset(0))
	}

	shaderNormal := getAttribLocation(r.Shader, "VERTEX_NORMAL")
	if shaderNormal >= 0 {
		gl.BindBuffer(gl.ARRAY_BUFFER, r.NormsVBO)
		gl.EnableVertexAttribArray(uint32(shaderNormal))
		gl.VertexAttribPointer(uint32(shaderNormal), 3, gl.FLOAT, false, 0, gl.PtrOffset(0))
	}

	shaderVertUv := getAttribLocation(r.Shader, "VERTEX_UV_0")
	if shaderVertUv >= 0 {
		gl.BindBuffer(gl.ARRAY_BUFFER, r.UvVBO)
		gl.EnableVertexAttribArray(uint32(shaderVertUv))
		gl.VertexAttribPointer(uint32(shaderVertUv), 2, gl.FLOAT, false, 0, gl.PtrOffset(0))
	}

	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.ElementsVBO)
	gl.DrawElements(gl.TRIANGLES, int32(r.FaceCount*3), gl.UNSIGNED_INT, gl.PtrOffset(0))
	gl.BindVertexArray(0)
}