Giter Site home page Giter Site logo

fauxgl's Introduction

FauxGL

3D software rendering in pure Go. No OpenGL, no C extensions, no nothin'.


Dragon

About

It's like OpenGL, but it's not. It's FauxGL.

It doesn't use your graphics card, only your CPU. So it's slow and unsuitable for realtime rendering. But it's still pretty fast. It works the same way OpenGL works - rasterizing.

Features

  • STL, OBJ, PLY, 3DS file formats
  • triangle rasterization
  • vertex and fragment "shaders"
  • view volume clipping
  • face culling
  • alpha blending
  • textures
  • triangle & line meshes
  • depth biasing
  • wireframe rendering
  • built-in shapes (plane, sphere, cube, cylinder, cone)
  • anti-aliasing (via supersampling)
  • voxel rendering
  • parallel processing

Performance

FauxGL uses all of your CPU cores. But none of your GPU.

Rendering the Stanford Dragon shown above (871306 triangles) at 1920x1080px takes about 150 milliseconds on my machine. With 4x4=16x supersampling, it takes about 950 milliseconds. This is the time to render a frame and does not include loading the mesh from disk.

Go Get

go get -u github.com/fogleman/fauxgl

Go Run

cd go/src/github.com/fogleman/fauxgl
go run examples/hello.go

Go Doc

https://godoc.org/github.com/fogleman/fauxgl

Complete Example

package main

import (
	. "github.com/fogleman/fauxgl"
	"github.com/nfnt/resize"
)

const (
	scale  = 1    // optional supersampling
	width  = 1920 // output width in pixels
	height = 1080 // output height in pixels
	fovy   = 30   // vertical field of view in degrees
	near   = 1    // near clipping plane
	far    = 10   // far clipping plane
)

var (
	eye    = V(-3, 1, -0.75)               // camera position
	center = V(0, -0.07, 0)                // view center position
	up     = V(0, 1, 0)                    // up vector
	light  = V(-0.75, 1, 0.25).Normalize() // light direction
	color  = HexColor("#468966")           // object color
)

func main() {
	// load a mesh
	mesh, err := LoadOBJ("examples/dragon.obj")
	if err != nil {
		panic(err)
	}

	// fit mesh in a bi-unit cube centered at the origin
	mesh.BiUnitCube()

	// smooth the normals
	mesh.SmoothNormalsThreshold(Radians(30))

	// create a rendering context
	context := NewContext(width*scale, height*scale)
	context.ClearColorBufferWith(HexColor("#FFF8E3"))

	// create transformation matrix and light direction
	aspect := float64(width) / float64(height)
	matrix := LookAt(eye, center, up).Perspective(fovy, aspect, near, far)

	// use builtin phong shader
	shader := NewPhongShader(matrix, light, eye)
	shader.ObjectColor = color
	context.Shader = shader

	// render
	context.DrawMesh(mesh)

	// downsample image for antialiasing
	image := context.Image()
	image = resize.Resize(width, height, image, resize.Bilinear)

	// save image
	SavePNG("out.png", image)
}

Teapot

fauxgl's People

Contributors

fogleman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fauxgl's Issues

files used in the examples missing

for example the teapot is dependent on the teapot-ply existing.

mesh, err := LoadPLY("examples/ply/teapot.ply")

If they are too big maybe you can add a reference in the readme to where you got it and the others failing example from.

I found one here, but not sure if this is the right one
https://people.sc.fsu.edu/~jburkardt/data/ply/teapot.ply

Hope this is not too much bother.. Its just alot of run playing around with this.

T-Junction solution

Hi @fogleman
I'm trying to wrap my head around your voxel optimization and triangulation code and can't seem to find the part that you referenced in your medium article:

"Each rectangular face starts with four edges. These edges are first segmented where T-junctions occur. We still have a quad, but these points indicate where we need the triangle vertices to be when we triangulate the quad. I use a recursive solution, repeatedly splitting the polygon into two polygons."

I was looking for recursion along these lines but can't find it:

fauxgl/voxel.go

Line 193 in 265867c

func triangulateVoxelFaces(plane voxelPlane, faces []voxelFace) []*Triangle {

Could you point me to the part where you split the polygons?

Please add LICENSE file to repo

@fogleman - this looks fantastic.

Can you please add a "LICENSE" file to the top-level directory of the repo?
Ideally, you would choose Apache-2.0, MIT, or BSD, but choosing one is important to enable others to use your open source repo.

Thank you!

How feasible is Mesh => []Voxel conversion?

Have you considered providing a func (m *Mesh) ToVoxels(box Box, nx, ny, nz int) []Voxel
where nx, ny, and nz are the number of voxels in the X, Y, and Z directions (enclosed by the provided bounding box)?

That way, a mesh could be voxelized at any desired resolution by subdividing the conversion into smaller boxes and recombining them later.

Import STL with model scale

Hi,
I would like to use fauxgl to render an top-down image of my 3D-models, where every pixel covers exactly 4.9cm at the y=0 plane.
My 3D models are all correctly scaled and when I normaly import them, the size is retained.
stl-import
Once I import a STL with fauxgl, it always scales to the same, predefined size.
How can I achieve correct scale of models after import using fauxgl?

Simplify not deterministic?

I already tried to find an simple answer in the code, but is the Simplify deterministic? I hope that not, because I get different results. ;) Maybe you can confirm or even explain. Thanks a lot for this great package!

Data race in rasterizer

Rasterizer is doing an unsafe data access operation that results in a race:

==================
WARNING: DATA RACE
Write at 0x00c0003012f0 by goroutine 8:
  github.com/fogleman/fauxgl.(*Context).rasterize()
      external/com_github_fogleman_fauxgl/context.go:252 +0x109c
  github.com/fogleman/fauxgl.(*Context).drawClippedTriangle()
      external/com_github_fogleman_fauxgl/context.go:347 +0xa49
  github.com/fogleman/fauxgl.(*Context).DrawTriangle()
      external/com_github_fogleman_fauxgl/context.go:387 +0x924
  github.com/fogleman/fauxgl.(*Context).DrawTriangles.func1()
      external/com_github_fogleman_fauxgl/context.go:421 +0xda
  github.com/fogleman/fauxgl.(*Context).DrawTriangles·dwrap·3()
      external/com_github_fogleman_fauxgl/context.go:426 +0x47

Previous read at 0x00c0003012f0 by goroutine 15:
  github.com/fogleman/fauxgl.(*Context).rasterize()
      external/com_github_fogleman_fauxgl/context.go:232 +0x99c
  github.com/fogleman/fauxgl.(*Context).drawClippedTriangle()
      external/com_github_fogleman_fauxgl/context.go:347 +0xa49
  github.com/fogleman/fauxgl.(*Context).DrawTriangle()
      external/com_github_fogleman_fauxgl/context.go:387 +0x924
  github.com/fogleman/fauxgl.(*Context).DrawTriangles.func1()
      external/com_github_fogleman_fauxgl/context.go:421 +0xda
  github.com/fogleman/fauxgl.(*Context).DrawTriangles·dwrap·3()
      external/com_github_fogleman_fauxgl/context.go:426 +0x47

Goroutine 8 (running) created at:
  github.com/fogleman/fauxgl.(*Context).DrawTriangles()
      external/com_github_fogleman_fauxgl/context.go:417 +0x7b
  github.com/fogleman/fauxgl.(*Context).DrawMesh()
      external/com_github_fogleman_fauxgl/context.go:436 +0x54
<skipped>

The code in question even has a comment:

            if dc.ReadDepth && bz > dc.DepthBuffer[i] { // safe w/out lock?
                continue
            }

I think, a sync.RWMutex around accesses to DepthBuffer would fix it.

NewVoxelMesh: index out of range panic

I've been encountering an "index out of range" panic when creating a VoxelMesh similar to the following:

voxels := []Voxel{
		{X: 0, Y: 2, Z: 0}, {X: 1, Y: 0, Z: 0},
		{X: 0, Y: 1, Z: 0}, {X: 1, Y: 1, Z: 0},
		{X: 0, Y: 2, Z: 0}, {X: 1, Y: 2, Z: 0},
		{X: 1, Y: 1, Z: 1},
	}
vm := NewVoxelMesh(voxels)

It seems to generally happen with any "plane" of voxels with a few more voxels above it.

This mesh (and other similar ones) reproduces the following error consistently:

panic: runtime error: index out of range [-1] [recovered]
panic: runtime error: index out of range [-1]
...
github.com/fogleman/fauxgl.combineVoxelFaces({0xc000114280?, 0x3, 0x1?})
/projects/github.com/fogleman/fauxgl/voxel.go:179 +0x545
github.com/fogleman/fauxgl.NewVoxelMesh({0xc00008ddd8, 0x7, 0x7?})
/projects/github.com/fogleman/fauxgl/voxel.go:93 +0x273

I am not sure if this is user error on my part or a bug as I'm very new to this library and the concept of voxel meshes.

Option to switch between sample and bilinearsample

In the shader fragment functions it uses BilinearSample to get the color but I noticed that this causes extremely small differences in the output image. I am using this package to render something that has to be exactly the same on every output and I noticed that the Sample function does not have this problem.

Not exactly experienced with stuff like this but would it cause problems to include an option in the shader to switch between the sampling types so I don't have to save an edited version of the code with mine?

Scale it

I was thinking about making this distributed using NATs or other infrastructure.

I guess it would get good to use a non tile based rendering process because this is a ray tracer and so each tile does not exist in isolation ?

Also there is a pretty good actors based golang implementation that supports remote objects and supervised trees.
This might be a better approach. It also makes it easier to map to the existing goroutines.

https://github.com/AsynkronIT/protoactor-go

Can you give me your own thoughts on this beforehand so I can pick the best approach.

Oh and why not Jocko too. That's another approach to share state but also events with many consumers.
https://github.com/travisjeffery/jocko
This provides the processing with results fed back into Kafka / Jocko
https://github.com/lovoo/goka

It's kind of interesting that with a Kafka approach that all state can be replayed. Boring functionality provided is the ability to stop the rendering and restart it.
Recover from a server dying and looking some part of a big single render.
Maybe other neat functionality by going down this path.

The main thing also to consider is how easy to refactor the code to be able to work with or without a distributed system. I think you would want fauxgl to still work. I think it's possible by either embedding the clustering inside a go routine ( possible with Nats and Nsq).

Are you cool about this going back into this code base ? Assuming you like the design. I had waste :)

Python port

Hi @fogleman. First off, really great job with this library, it's so simple and educational. This probably shouldn't be raised as an issue, but just wanted to let you know that I made a direct port of fauxgl for Python (with full credits to you), called phauxgl. It's obiously much slower since it's pure Python, and I will optimize and add remaining features in the future. Although slow, I can think of quite a few use-cases and projects where it will be directly useful, so thanks for writing this library.
I thought this may or may not be interesting for you and/or fauxgl users.

view full object in image programatically?

is there an api call I can use to make it so for every image I load, it uses some heuristic to make most of the object appear in the image? Right now most of my images only show partial views of the STLs. I want to reliably see most of the object in the image.

Not writing pixel when alpha transparency?

I noticed a potential issue. Inside context.go, there's an if/else clause for writing the final pixel value. When there's no alphablending it writes the pixel using dc.ColorBuffer.SetNRGBA(x, y, color.NRGBA()).

However, when there is alpha blending, there's some code to compute the alpha blended color, but it never actually writes the pixel value. If I'm understanding it right, that could be fixed by adding a single line of code. I had to add a line writing the pixel to get it right when I ported your library to Python.

Here is the relevant code block, line 221 of context.go:

			if dc.WriteColor {
				// update color buffer
				if dc.AlphaBlend && color.A < 1 {
					sr, sg, sb, sa := color.NRGBA().RGBA()
					a := (0xffff - sa) * 0x101
					j := dc.ColorBuffer.PixOffset(x, y)
					dr := &dc.ColorBuffer.Pix[j+0]
					dg := &dc.ColorBuffer.Pix[j+1]
					db := &dc.ColorBuffer.Pix[j+2]
					da := &dc.ColorBuffer.Pix[j+3]
					*dr = uint8((uint32(*dr)*a/0xffff + sr) >> 8)
					*dg = uint8((uint32(*dg)*a/0xffff + sg) >> 8)
					*db = uint8((uint32(*db)*a/0xffff + sb) >> 8)
					*da = uint8((uint32(*da)*a/0xffff + sa) >> 8)
				} else {
					dc.ColorBuffer.SetNRGBA(x, y, color.NRGBA())
				}

How to rorate stl....

i'm sorry about this... but i had cost more then 2 days on it...

there's two problems about it , and i almost get first

  1. Suit the png size, I use BiUnitCube, but sometimes the graph would over png's size..
    But that's accepted

  2. When I set camera ... the graph can't be level...

such as this

Ghosting_Test_Cube stl

this's i want

WX20200202-000652@2x

i try Scale and Rorota after LookAt...

and ... it been odd.......

Thx !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.