Refraction with pyluxcore

Use this forum for general user support and related questions.
mick
Posts: 60
Joined: Mon May 21, 2018 7:57 pm

Refraction with pyluxcore

Post by mick » Wed May 30, 2018 4:28 pm

I can't get refraction working. I guess it has to do with the mesh.

How do I specify what is the interior vs the exterior of an object? My assumption it is done by the order of the vertices for each face. Looking from inside ordering them clockwise, or is it the other way around?

Here the code for a simple model: a prism between a spot and a plane. There is no visible effect of refraction. The comments show some stuff I tried.

Any other remarks on the code appreciated.

Code: Select all

import sys
sys.path.append('/home/mick/dev/DLO/lib')
import pyluxcore
from time import sleep
import array

import imageio
import numpy as np
from scipy import stats, signal

from common import SP
from prism import make_prism_mesh
from luxcore import add_object


def main():
  pyluxcore.Init()
  scene = pyluxcore.Scene()

  props = pyluxcore.Properties()
  if False:
    props.SetFromString("""
      scene.volumes.vol_air.type = clear
      scene.volumes.vol_air.absorption = 0.0 0.0 0.0
      scene.volumes.vol_air.asymmetry = 0.0 0.0 0.0
      scene.world.volume.default = vol_air
    """)
  else:
    props.SetFromString("""
      scene.volumes.vol_air.type = homogeneous
      scene.volumes.vol_air.multiscattering = 0
      scene.volumes.vol_air.scattering = 0. 0. 0.01
      scene.volumes.vol_air.absorption = 0.01 0.01 0.01
      scene.volumes.vol_air.asymmetry = 0.0 0.0 0.0
      scene.world.volume.default = vol_air
    """)
  scene.Parse(props)

  prism_vol = "lens_vol"
  props = pyluxcore.Properties()
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".type", "homogeneous"))
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".scattering", [0.01, 0, 0]))
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".multiscattering", 0))

  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".type", "clear"))
  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".absorption", [0,0,0]))
  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".ior", SP["prism_ior"]))
  #props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".priority", 255))
  scene.Parse(props)

  prism_mat = "lens"
  props = pyluxcore.Properties()
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".type", "glass"))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".transparency", 0.8))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".interiorior", 1.5))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".exteriorior", 1.0))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".kt", [0.5, 0.5, 0.5]))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".kr", [0.5, 0.5, 0.5]))
  # props.Set(pyluxcore.Property("scene.materials."+prism_mat+".cauchyc", 0.1))
  # props.Set(pyluxcore.Property("scene.materials."+prism_mat+".volume.interior", prism_vol))
  scene.Parse(props)

  # prism = make_prism_mesh(1, 2, 0, -0.1, 0, 2)

  vertices = [
    (-0.7, -0.7, 0),
    (1, -1, 0),
    (0.1, 1, 0),
    (-0.7, -0.7, 2),
    (1, -1, 2),
    (0.1, 1, 2),
  ]
  if True:
    # clockwise
    faces = [
      (0, 2, 1),
      (0, 1, 3),
      (1, 4, 3),
      (1, 2, 4),
      (2, 5, 4),
      (2, 0, 5),
      (0, 3, 5),
      (3, 5, 4),
    ]
  else:
    faces = [
      (0, 1, 2),
      (0, 3, 1),
      (1, 3, 4),
      (1, 4, 2),
      (2, 4, 5),
      (2, 5, 0),
      (0, 5, 3),
      (3, 4, 5),
    ]
  prism = (vertices, faces)

  name = "lens0"
  scene.DefineMesh(name, prism[0], prism[1], None, None, None, None, None)
  add_object(scene, name, name, prism_mat, visible=True)

  name = "background"
  obj_props = pyluxcore.Properties()
  obj_props.SetFromString("""
        scene.materials.{name}.type = matte
        scene.materials.{name}.kd = 0.5 0.5 0.5
        """.format(name=name))
  scene.Parse(obj_props)
  vertices = [
    (-10, 4, -10),
    (10, 4, -10),
    (10, 4, 10),
    (-10, 4, 10)
  ]
  faces = [
    (0, 1, 2),
    (2, 3, 0)
  ]
  scene.DefineMesh(name, vertices, faces, None, None, None, None, None)
  add_object(scene, name, name, name, True)

  props = pyluxcore.Properties()
  props.SetFromString("""
    scene.camera.lookat.orig = 0.5 -9 9.2
    scene.camera.lookat.target = 0 2 1
    scene.camera.up = 0 0 1
  """)
  scene.Parse(props)

  # props = pyluxcore.Properties()
  # props.SetFromString("""
  #   scene.lights.s.type = sky2
  # """)
  # scene.Parse(props)
  props = pyluxcore.Properties()
  props.SetFromString("""
    scene.lights.l.type = spot
    scene.lights.l.position = 0 -5 1
    scene.lights.l.target = 0 0 1
    #scene.lights.l.radius = 0.04
    scene.lights.l.coneangle = 15
    scene.lights.l.conedeltaangle = 1
    scene.lights.l.gain = 2000.0 2000.0 2000.0
  """)
  scene.Parse(props)

  session = render(scene, 800)
  save_rendering(session)


def build_session(scene, pixels):
  config_props = pyluxcore.Properties()
  config_props.SetFromString("""
    #renderengine.type = TILEPATHCPU
    ## This sampler must be used with this engine, no other samplers work
    #sampler.type = TILEPATHSAMPLER
    ## This controls the amount of samples per pixel
    #tilepath.sampling.aa.size = 3
    ## Stop once all pixels were sampled, do not start another pass
    #tile.multipass.enable = 0
    renderengine.type = PATHCPU
    sampler.type = SOBOL
    path.pathdepth.total = 20
    path.pathdepth.diffuse = 20
    path.pathdepth.glossy = 20
    path.pathdepth.specular = 20
    path.russianroulette.depth = 99
    path.russianroulette.cap = 0.
    #lightstrategy.type = POWER
    renderengine.seed = 11
    film.width = """ + str(pixels) + """
    film.height = """ + str(pixels) + """
    # The first plugin: the linear tonemapper multiplies the pixel colors with the scale
    film.imagepipelines.0.0.type = TONEMAP_AUTOLINEAR
    # The second plugin: gamma correction
    film.imagepipelines.0.1.type = GAMMA_CORRECTION
    film.imagepipelines.0.1.value = 2.2
    
    # Pipeline 1, plugin 0
    #film.imagepipelines.1.0.type = TONEMAP_LINEAR
    #film.imagepipelines.1.0.scale = 5e-5
    # Pipeline 1, plugin 1
    film.imagepipelines.1.1.type = CONTOUR_LINES
    #film.imagepipelines.1.1.scale = 3e-1
    film.imagepipelines.1.1.range = """ + str(179*715) + """
    film.imagepipelines.1.1.steps = 10
    film.imagepipelines.1.1.zerogridsize = 8
    # Pipeline 1, plugin 2
    film.imagepipelines.1.2.type = GAMMA_CORRECTION
    film.imagepipelines.1.2.value = 2.2
    
    # This is the pipeline with the tonemapped image as usual
    film.outputs.0.type = RGB_IMAGEPIPELINE
    film.outputs.0.filename = image0.png
    # You have to specify which imagepipeline you want as output
    film.outputs.0.index = 0
    
    # This is the pipeline with the visible contour lines
    film.outputs.1.type = RGB_IMAGEPIPELINE
    film.outputs.1.filename = image1.png
    # You have to specify which imagepipeline you want as output
    film.outputs.1.index = 1
    
    film.outputs.2.type = IRRADIANCE
    film.outputs.2.filename = image2.hdr
    """)

  renderconfig = pyluxcore.RenderConfig(config_props, scene)
  session = pyluxcore.RenderSession(renderconfig)
  return session

def render(scene, pixels):
  session = build_session(scene, pixels)
  session.Start()
  for i in range(3):
    sleep(1)
    #session.WaitForDone()
    # session.UpdateStats()
    # stats = session.GetStats()
    # print("[Elapsed time: %3dsec][Samples %4d][Avg. samples/sec % 3.2fM on %.1fK tris]" % (
    #   stats.Get("stats.renderengine.time").GetFloat(),
    #   stats.Get("stats.renderengine.pass").GetInt(),
    #   (stats.Get("stats.renderengine.total.samplesec").GetFloat() / 1000000.0),
    #   (stats.Get("stats.dataset.trianglecount").GetFloat())))# / 1000.0)))
    #print(stats)
  session.UpdateStats()
  session.Stop()
  return session

def save_rendering(session):
  session.GetFilm().Save()
  pixels = session.GetFilm().GetWidth()
  buffer = array.array("f", [0.0]) * (pixels * pixels * 3)
  session.GetFilm().GetOutputFloat(pyluxcore.FilmOutputType.IRRADIANCE, buffer, 1)
  npa = np.array(buffer).reshape(pixels, pixels, 3)[::-1]
  print(stats.describe(signal.medfilt(npa.reshape(pixels * pixels, 3))))
  print("MEAN:{} MAX:{}".format(npa.mean(), npa.max()))
  imageio.imwrite("image5.png", npa)


if __name__ == "__main__":
  main()

User avatar
B.Y.O.B.
Developer
Posts: 1825
Joined: Mon Dec 04, 2017 10:08 pm
Location: Germany
Contact:

Re: Refraction with pyluxcore

Post by B.Y.O.B. » Wed May 30, 2018 4:37 pm

It is decided by the normal vector: the side of the triangle where the normal is pointing is the exterior, the other side the interior.
You can specify the normals when defining the mesh.
I hope this helps, I don't know how they should be specified in detail, I guess it is a list of 3-element vectors (probably a flat list).
Remember to ensure that the normals are normalized.
Support LuxCoreRender project with salts and bounties

mick
Posts: 60
Joined: Mon May 21, 2018 7:57 pm

Re: Refraction with pyluxcore

Post by mick » Thu May 31, 2018 1:23 am

OK, I implemented the normals. Just wondering why this is necessary, as it can be calculated quickly from the faces.

I checked them visually with the build_normal() function.

Still the result does not look like I expect. There is no indication of refraction behind the prism.

Also the lightning pattern on/in the prism is very strange. Despite symmetric lightning and camera (x=0) rendering always brings the same asymmetric pattern.

I'm completely lost. This output does not make any sense to me.

Here the code to reproduce:

Code: Select all

import sys
sys.path.append('/home/mick/dev/DLO/lib')
import pyluxcore
from time import sleep
import array

import imageio
import numpy as np
from scipy import stats, signal

from common import SP
from prism import make_prism_mesh
from luxcore import add_object, define_mesh


def main():
  pyluxcore.Init()
  scene = pyluxcore.Scene()

  props = pyluxcore.Properties()
  if False:
    props.SetFromString("""
      scene.volumes.vol_air.type = clear
      scene.volumes.vol_air.absorption = 0.0 0.0 0.0
      scene.volumes.vol_air.asymmetry = 0.0 0.0 0.0
      scene.world.volume.default = vol_air
    """)
  else:
    props.SetFromString("""
      scene.volumes.vol_air.type = homogeneous
      scene.volumes.vol_air.multiscattering = 0
      scene.volumes.vol_air.scattering = 0.001 0.001 0.001
      scene.volumes.vol_air.absorption = 0. 0. 0.
      scene.volumes.vol_air.asymmetry = 0.0 0.0 0.0
      scene.world.volume.default = vol_air
    """)
  scene.Parse(props)

  prism_vol = "lens_vol"
  props = pyluxcore.Properties()
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".type", "homogeneous"))
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".scattering", [0.01, 0, 0]))
  # props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".multiscattering", 0))

  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".type", "clear"))
  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".absorption", [0,0,0]))
  props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".ior", 1.4580))
  #props.Set(pyluxcore.Property("scene.volumes."+prism_vol+".priority", 255))
  scene.Parse(props)

  prism_mat = "lens"
  props = pyluxcore.Properties()
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".type", "glass"))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".kt", [0.5, 0.5, 0.5]))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".kr", [0.5, 0.5, 0.5]))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".transparency", 0.5))
  # props.Set(pyluxcore.Property("scene.materials."+prism_mat+".interiorior", 1.5))
  # props.Set(pyluxcore.Property("scene.materials."+prism_mat+".exteriorior", 1.0))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".volume.interior", prism_vol))
  props.Set(pyluxcore.Property("scene.materials."+prism_mat+".cauchyc", 0.00354))
  scene.Parse(props)

  # prism = make_prism_mesh(1, 2, 0, -0.1, 0, 2)

  # vertices = [
  #   (0, -1, 0),
  #   (1, 1, 0),
  #   (-1, 1, 0),
  #   (0, -1, 2),
  #   (1, 1, 2),
  #   (-1, 1, 2),
  # ]
  vertices = [
    (-1, -1, 0),
    (1, -1, 0),
    (0, 1, 0),
    (-1, -1, 2),
    (1, -1, 2),
    (0, 1, 2),
  ]
  faces = [
    (0, 2, 1),
    (0, 1, 3),
    (1, 4, 3),
    (1, 2, 4),
    (2, 5, 4),
    (2, 0, 5),
    (0, 3, 5),
    (3, 4, 5),
  ]
  prism = (vertices, faces)

  name = "lens0"
  define_mesh(scene, name, prism[0], prism[1])
  add_object(scene, name, name, prism_mat, visible=True)

  name = "background"
  obj_props = pyluxcore.Properties()
  obj_props.SetFromString("""
        scene.materials.{name}.type = matte
        scene.materials.{name}.kd = 0.5 0.5 0.5
        """.format(name=name))
  scene.Parse(obj_props)
  vertices = [
    (-10, 4, -10),
    (10, 4, -10),
    (10, 4, 10),
    (-10, 4, 10)
  ]
  faces = [
    (0, 1, 2),
    (2, 3, 0)
  ]
  scene.DefineMesh(name, vertices, faces, None, None, None, None, None)
  add_object(scene, name, name, name, True)

  props = pyluxcore.Properties()
  props.SetFromString("""
    scene.camera.lookat.orig = 0 -4 9.2
    scene.camera.lookat.target = 0 2 1
    scene.camera.up = 0 0 1
  """)
  scene.Parse(props)

  # props = pyluxcore.Properties()
  # props.SetFromString("""
  #   scene.lights.s.type = sky2
  # """)
  # scene.Parse(props)
  props = pyluxcore.Properties()
  props.SetFromString("""
    scene.lights.l.type = laser
    scene.lights.l.position = 0 -5 1
    scene.lights.l.target = 0 7 1
    scene.lights.l.radius = 3
    # scene.lights.l.coneangle = 15
    # scene.lights.l.conedeltaangle = 1
    scene.lights.l.gain = 2000.0 2000.0 2000.0
    scene.lights.l2.type = spot
    scene.lights.l2.position = 0 0 10
    scene.lights.l2.target = 0 0 0
    scene.lights.l2.coneangle = 90
    # scene.lights.l2.conedeltaangle = 1
    scene.lights.l2.gain = 20.0 20.0 20.0
  """)
  scene.Parse(props)

  session = render(scene, 800)
  save_rendering(session)


def build_session(scene, pixels):
  config_props = pyluxcore.Properties()
  config_props.SetFromString("""
    #renderengine.type = TILEPATHCPU
    ## This sampler must be used with this engine, no other samplers work
    #sampler.type = TILEPATHSAMPLER
    ## This controls the amount of samples per pixel
    #tilepath.sampling.aa.size = 3
    ## Stop once all pixels were sampled, do not start another pass
    #tile.multipass.enable = 0
    renderengine.type = PATHCPU
    sampler.type = SOBOL
    path.pathdepth.total = 20
    path.pathdepth.diffuse = 20
    path.pathdepth.glossy = 20
    path.pathdepth.specular = 20
    path.russianroulette.depth = 99
    path.russianroulette.cap = 0.
    #lightstrategy.type = POWER
    #renderengine.seed = 11
    film.width = """ + str(pixels) + """
    film.height = """ + str(pixels) + """
    # The first plugin: the linear tonemapper multiplies the pixel colors with the scale
    film.imagepipelines.0.0.type = TONEMAP_AUTOLINEAR
    # The second plugin: gamma correction
    film.imagepipelines.0.1.type = GAMMA_CORRECTION
    film.imagepipelines.0.1.value = 2.2
    
    # Pipeline 1, plugin 0
    #film.imagepipelines.1.0.type = TONEMAP_LINEAR
    #film.imagepipelines.1.0.scale = 5e-5
    # Pipeline 1, plugin 1
    film.imagepipelines.1.1.type = CONTOUR_LINES
    #film.imagepipelines.1.1.scale = 3e-1
    film.imagepipelines.1.1.range = """ + str(179*715) + """
    film.imagepipelines.1.1.steps = 10
    film.imagepipelines.1.1.zerogridsize = 8
    # Pipeline 1, plugin 2
    film.imagepipelines.1.2.type = GAMMA_CORRECTION
    film.imagepipelines.1.2.value = 2.2
    
    # This is the pipeline with the tonemapped image as usual
    film.outputs.0.type = RGB_IMAGEPIPELINE
    film.outputs.0.filename = image0.png
    # You have to specify which imagepipeline you want as output
    film.outputs.0.index = 0
    
    # This is the pipeline with the visible contour lines
    film.outputs.1.type = RGB_IMAGEPIPELINE
    film.outputs.1.filename = image1.png
    # You have to specify which imagepipeline you want as output
    film.outputs.1.index = 1
    
    film.outputs.2.type = IRRADIANCE
    film.outputs.2.filename = image2.hdr
    """)

  renderconfig = pyluxcore.RenderConfig(config_props, scene)
  session = pyluxcore.RenderSession(renderconfig)
  return session

def render(scene, pixels):
  session = build_session(scene, pixels)
  session.Start()
  for i in range(3):
    sleep(1)
    #session.WaitForDone()
    # session.UpdateStats()
    # stats = session.GetStats()
    # print("[Elapsed time: %3dsec][Samples %4d][Avg. samples/sec % 3.2fM on %.1fK tris]" % (
    #   stats.Get("stats.renderengine.time").GetFloat(),
    #   stats.Get("stats.renderengine.pass").GetInt(),
    #   (stats.Get("stats.renderengine.total.samplesec").GetFloat() / 1000000.0),
    #   (stats.Get("stats.dataset.trianglecount").GetFloat())))# / 1000.0)))
    #print(stats)
  session.UpdateStats()
  session.Stop()
  return session

def save_rendering(session):
  session.GetFilm().Save()
  pixels = session.GetFilm().GetWidth()
  buffer = array.array("f", [0.0]) * (pixels * pixels * 3)
  session.GetFilm().GetOutputFloat(pyluxcore.FilmOutputType.IRRADIANCE, buffer, 1)
  npa = np.array(buffer).reshape(pixels, pixels, 3)[::-1]
  print(stats.describe(signal.medfilt(npa.reshape(pixels * pixels, 3))))
  print("MEAN:{} MAX:{}".format(npa.mean(), npa.max()))
  imageio.imwrite("image5.png", npa)


if __name__ == "__main__":
  main()

Code: Select all

import sys
import array
import numpy as np
from scipy import stats, signal
import imageio
from time import sleep


sys.path.append('/home/mick/dev/DLO/lib')
import pyluxcore


def add_object(scene, obj_name, mesh_name, mat_name, visible=True):
  obj_props = pyluxcore.Properties()
  obj_props.Set(pyluxcore.Property("scene.objects." + obj_name + ".shape", mesh_name))
  obj_props.Set(pyluxcore.Property("scene.objects." + obj_name + ".material", mat_name))
  obj_props.Set(pyluxcore.Property("scene.objects." + obj_name + ".camerainvisible", not visible))
  scene.Parse(obj_props)

def define_mesh(scene, name, vertices, faces, transform=None):
  normals = []
  verts = np.array(vertices, dtype=np.float)
  for f in faces:
    a = verts[f[1]] - verts[f[0]]
    b = verts[f[2]] - verts[f[0]]
    n = np.cross(a,b)
    n /= np.linalg.norm(n)
    # build_normal(scene, f, verts, n)
    normals.append(tuple(n.tolist()))
  scene.DefineMesh(name, vertices, faces, normals, None, None, None, None)

def build_normal(scene, face, verts, normal):
  props = pyluxcore.Properties()
  props.SetFromString("""
      scene.materials.{name}.matte = clear
      scene.materials.{name}.kd = 1.0 0.0 0.0
    """.format(name="normal"))
  scene.Parse(props)
  face = np.array([verts[face[i]] for i in range(3)], dtype=float)
  r = face.mean(axis=0)
  p = r + normal
  rs = (face + 9*r) / 10
  vertices = [tuple(r.tolist()) for r in rs] + [tuple(p.tolist())]
  faces = [
    (0, 1, 2),
    (0, 1, 3),
    (1, 2, 3),
    (2, 0, 3),
  ]
  name = "normal_"+str(np.random.randint(0, 9999999))
  scene.DefineMesh(name, vertices, faces, None, None, None, None, None)
  add_object(scene, name, name, "normal", True)

User avatar
B.Y.O.B.
Developer
Posts: 1825
Joined: Mon Dec 04, 2017 10:08 pm
Location: Germany
Contact:

Re: Refraction with pyluxcore

Post by B.Y.O.B. » Thu May 31, 2018 8:45 am

I can't test your code because it's grown too much ;)
Please post a small standalone example that does not require the "common", "prism" or "luxcore" modules.

It is probably also a good idea to put your code into a VCS, e.g. on github, so I can simply clone it.

A tip for debugging your geometry:
Set the renderengine to FILESAVER in TXT mode: https://wiki.luxcorerender.org/LuxCore_ ... LESAVER.22
This will generate the .cfg and .scn files, but more importantly the .ply files of your meshes.
You can then load the .ply files into a 3D program like Blender or MeshLab and check there if the geometry is correct and well-formed.
Support LuxCoreRender project with salts and bounties

mick
Posts: 60
Joined: Mon May 21, 2018 7:57 pm

Re: Refraction with pyluxcore

Post by mick » Thu May 31, 2018 12:25 pm

Thanks for the tip with FILESAVER. Looking at the ply file I recognized that normals are specified for each vertex not for each face. Is this right?

So what is the requirement of theses normals then to define a valid prism/lens object? Should it be the mean of the adjoint faces, or just point somewhere outside the hull, or what else?

User avatar
B.Y.O.B.
Developer
Posts: 1825
Joined: Mon Dec 04, 2017 10:08 pm
Location: Germany
Contact:

Re: Refraction with pyluxcore

Post by B.Y.O.B. » Thu May 31, 2018 1:15 pm

If you just need flat shading (no interpolation between faces), then you just need to copy the face normal as your vertex normal.
You can calculate the face normal with the cross product of two of your triangle edges, like it's done here:
https://github.com/LuxCoreRender/LuxCor ... r.cpp#L485
You have to normalize it and then you can use it for the three vertices of the triangle.

If you need smooth shading (interpolating over faces to fake a smooth surface), it gets more complicated, I can't help you there.
Attachments
scrn_2018-05-31_15-11-37.png
blue: vertex normals
scrn_2018-05-31_15-11-37.png (6.27 KiB) Viewed 711 times
Support LuxCoreRender project with salts and bounties

mick
Posts: 60
Joined: Mon May 21, 2018 7:57 pm

Re: Refraction with pyluxcore

Post by mick » Thu May 31, 2018 1:55 pm

So for the face normals that is exactly what I did. Then I tried to mean the (unnormalized) face normals to single (normalized) vertex normals. Which I listed instead of the face normals.

You say I should copy the face normals as vertex normal. Do you mean each vertex has multiple normals then? How is the list of normals structured then?

First I thought then normals would be a list of 3-tuples with the length of faces, for the second approach with the length of vertices. Now I understand that I have to provide face normals (1 per face) and vertex normals (n per vertex). Is it a list of lists of tuples? Where is it defined?

Code: Select all

DefineMesh(...)
    DefineMesh( (Scene)arg1, (str)arg2, (object)arg3, (object)arg4, (object)arg5, (object)arg6, (object)arg7, (object)arg8 [, (object)arg9]) -> None
luxcore API says:

Code: Select all

n	is a pointer to an array of normals. It can be NULL.
And ply spec list nx,ny,nz as float properties of vertex.

User avatar
B.Y.O.B.
Developer
Posts: 1825
Joined: Mon Dec 04, 2017 10:08 pm
Location: Germany
Contact:

Re: Refraction with pyluxcore

Post by B.Y.O.B. » Thu May 31, 2018 2:42 pm

In the case of flat shading, the faces do not share vertices.
So on the corners of the prism in my example image, you have 3 vertices on the same position, one for each face.
Each vertex has only one vertex normal.
Attachments
scrn_2018-05-31_16-46-02.png
scrn_2018-05-31_16-46-02.png (7.06 KiB) Viewed 699 times
Support LuxCoreRender project with salts and bounties

mick
Posts: 60
Joined: Mon May 21, 2018 7:57 pm

Re: Refraction with pyluxcore

Post by mick » Thu May 31, 2018 2:55 pm

Sorry, I do not understand what you mean.

I need to specify my object in a way that refraction and maybe with dispersion is calculated correctly. So I have to specify interior and exterior volumes, or interior and exterior IORs. But in any case I need to specify which side of a face ist interior and which is exterior. I understand that this is done with the normals, which point outside.

Do you mean that if I want to get refraction then I have duplicate the vertices to give each face exclusive vertices, and then set the normal of each vertices to the normal of its single face???

User avatar
B.Y.O.B.
Developer
Posts: 1825
Joined: Mon Dec 04, 2017 10:08 pm
Location: Germany
Contact:

Re: Refraction with pyluxcore

Post by B.Y.O.B. » Thu May 31, 2018 3:07 pm

mick wrote:
Thu May 31, 2018 2:55 pm
Do you mean that if I want to get refraction then I have duplicate the vertices to give each face exclusive vertices, and then set the normal of each vertices to the normal of its single face???
As far as I know that is the correct way, yes.
It has nothing to do with refraction though, it's just the way flat shaded meshes are defined.
But I usually don't deal with this low-level stuff, so I'm not an expert here.
Please have a look how it is done in the DefineBlenderMesh function I linked above.
Support LuxCoreRender project with salts and bounties

Post Reply