前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VTK:实现光照效果,从一根线到一个面

VTK:实现光照效果,从一根线到一个面

作者头像
曾高飞
发布2022-02-17 00:37:36
1.2K1
发布2022-02-17 00:37:36
举报

  导读:很多地方需要查看光照度什么的,目前也有很多软件来处理。今天就做了一个关于光照效果的内容。从早上10点开始,到下午3点半,开始写头条号。大概就这么长时间。中午没有休息,没有吃饭。

  内容:

  1、从一个最简单的案例说起

  2、我的Lighting类

  3、基本参数设置

  4、网格导入和基本初始化(update_grid函数)

  5、设置光线(update_lights函数)

  6、设置一个场来显示光的作用(update_shadow函数)

  7、结果的闪亮登场 可视化

  8、结束(附全部代码)

  一个简单的操作,如果没有变化,重复一万次,也还是只是一个操作。

  一个简单的操作,如果有2个变量,重复一万次,就是一个系统。

  1、从一个简单的案例说起

  pyvita里有一个案例。寻找从一个点出发与网格相交点的例子。

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  如图,蓝色的是一根线,与球面相交用点来显示。

  这个简单的案例,我们可以先分析一下,为了获得面上的交点,我们需要知道什么。

  (1)需要一个起始点

  start

  (2)需要一个终点,

  end,

  (3)需要一条直线,

  (4)需要明确一个面

  (5)求交点

  我们看一下这个简单案例的代码:

  import pyvista as pv
  import numpy as np
  # Create source to ray trace
  sphere=pv.Sphere(radius=0.85)
  # Define line segment
  start=[0, 0, 0]
  stop=[0.25, 1, 0.5]
  # Perform ray trace
  points, ind=sphere.ray_trace(start, stop)
  # # Create geometry to represent ray trace
  ray=pv.Line(start, stop)
  intersection=pv.PolyData(points)
  #
  # # Render the result
  p=pv.Plotter()
  p.add_mesh(sphere,
  show_edges=True, opacity=0.5, color="w",
  lighting=False, label="Test Mesh")
  p.add_mesh(ray, color="blue", line_width=5, label="Ray Segment")
  p.add_mesh(intersection, color="maroon",
  point_size=25, label="Intersection Points")
  p.add_legend()
  p()

  其实,5,8,9,12这四行就完成了主要的操作。

  Line 5:定义了一个球面网格

  Line 8-9:定义了起点和终点

  Line 12,:ray_trace函数获取交点。

  后面都是一些现实的内容。

  2、我的Lighting类

  我构造了一个类,完全按上面的简单的案例来操作。形成下图的光的效果。

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  后面蓝色的圆,是终点的显示。

  我们先看一下基本结构:

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  除了一些基本的参数之外,只有三个步骤,也就对应上面的三个函数:

  update_grid

  update_lights

  update_shadow

  3、基本参数设置

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  在__init__函数里,我们定义了9个参数。

  输入参数

  source 表示点光源的坐标

  angle 表示了,点光源扩散的角度,形成一个锥

  direction 表示光中心的方向,是一个单位向量

  seeds 是为了获取光终点坐标需要的一个数量的参数

  除了这四个之外,类的实例变量增加了

  grid 用于存储网格数据

  distance 设置光锥的长度

  nlights 记录光线的个数

  seed_polydata 是存储光点的可视化的数据

  为了计算两个点之间的距离,我定义了个mag函数

  @staticmethod
  def mag(start,end):
  s=(start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2 + (start[2] - end[2]) ** 2
  return s ** 0.5

  库里应该是有类似的函数的,不过我就懒得去查了。

  还有两个属性

  @property
  def source_polydata(self):
  return pv.PolyData(self.source)
  @property
  def aim_radius(self):
  return np.tan(self.angle) * self.distance

  第一个生成点光源的可视化数据

  第二个,aim_radius是目标终点的半径。

  我们是在从点光源出发之后的,底面上散列点,来确定光线个数的。

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  4、网格导入和基本初始化(update_grid函数)

  第一件事,我们得处理一下网格。并且初始化一些游戏下载数据,比如distance之类的。

  我们先看一下函数代码:

  def update_grid(self,filename):
  self.grid=pv.read(filename)
  xmin,xmax,ymin,ymax,zmin,zmax=self.grid.bounds
  start=[xmin,ymin,zmin]
  end=[xmax,ymax,zmax]
  dis=self.mag(start,end)
  p=[[xmin, ymin, zmin],
  [xmin, ymin, zmax],
  [xmin, ymax, zmin],
  [xmin, ymax, zmax],
  [xmax, ymin, zmin],
  [xmax, ymin, zmax],
  [xmax, ymax, zmin],
  [xmax, ymax, zmax]
  ]
  for i in p:
  self.distance=max(dis, self.mag(self.source, i))
  self.distance=self.distance*1.05

  (1)从文件读取网格

  (2)计算距离,以确定点光源发出的光线和网格的所有的位置要相交

  5、设置光线(update_lights函数)

  也就是说我们需要提取那些终点的数据。看一下代码:

  def update_lights(self):
  # 生成面
  # 终点中心
  end_center=[self.source[0] + self.distance * self.direction[0],
  self.source[1] + self.distance * self.direction[1],
  self.source[2] + self.distance * self.direction[2]
  ]
  plane=pv.Plane(center=end_center, direction=self.direction,
  i_size=2 * self.aim_radius, j_size=2 * self.aim_radius,
  i_resolution=self.seeds, j_resolution=self.seeds)
  # 定义终点
  stops=[]
  for point in plane.points:
  x1=point[0]
  y1=point[1]
  z1=point[2]
  if self.mag(point, end_center) < self.aim_radius:
  stops.append([x1, y1, z1])
  self.nlights=len(stops)
  self.lights=stops
  self.seed_polydata=pv.PolyData(stops)

  (1)生成一个以底面中心为中心的平面数据。

  (2)提取出属于球内的点的数据

  (3)生成一个polydata数据,用于可视化

  6、设置一个场来显示光的作用(update_shadow函数)

  这个名字可能不是很好,光照的地方居然用了shadow这个单词。

  同样先看代码:

  def update_shadow(self):
  shadow_cell_ids=[]
  for i in range(self.nlights):
  point, ind=self.grid.ray_trace(self.source, self.lights[i])
  if len(point) < 1:
  pass
  else:
  a=self.mag(self.source, point[0])
  end=ind[0]
  for i in point:
  if self.mag(self.source, i) < a:
  a=self.mag(self.source, i)
  end=ind
  else:
  end=ind[0]
  shadow_cell_ids.append(end)
  # 创建shadow cell属性
  shadow=np.zeros((self.grid.n_cells,))
  for i in shadow_cell_ids:
  shadow[i]=10
  self.grid.cell_arrays['shadow']=shadow

  (1)生成一个id列表,这个id是由ray_trace生成的,是面的编号列表。

  同时,需要说明的是,ray_trace会和面交两个以上的点,我们选择那些最近的,因为光线不会穿越物体。

  (2)生成一个shadow的标量属性,在列表内的面,赋值为10,其他为0.

  7、结果的闪亮登场 可视化

  最后,我们其实综合写了一个update函数。

  def update(self,filename):
  self.update_grid(filename)
  self.update_lights()
  self.update_shadow()

  主要是为了书写方便。

  看一下我们调用的方式:

  if __name__=='__main__':
  source=[3000,0,-2000]
  angle=20
  direction=[-0.3,0.1,1]
  seeds=100
  light=Lighting(source,angle,direction,seeds)
  light.update('car.stl')
  p=pv.Plotter()
  p.add_background_image('road.jpg')
  #
  p.add_mesh(light.source_polydata, color="yellow", point_size=20, render_points_as_spheres=True)
  p.add_mesh(light.seed_polydata, color="blue", line_width=5, label="Ray Segment")
  p.add_mesh(light.grid, scalars='shadow')
  # p.add_legend()
  p()

  设置了四个参数,同时调用update函数,读取了car.stl文件。并且显示出来。

  结果就是如上面我们说的那样的效果了。

VTK:实现光照效果,从一根线到一个面
VTK:实现光照效果,从一根线到一个面

  8、结论

  一个小小的功能,放在实际应用中,都会有很多不可思议的东西。其实很多时候,我们所用到的知识并不是那么多。更多的可能是组织形式。

  我是张麟博士,大家记得关注,如果大家对什么感兴趣,也可以在留言区回复。

  祝好!

  最后附上所有的代码:

  import pyvista as pv
  import numpy as np
  class Lighting:
  def __init__(self, source, angle, direction, seeds=10):
  self.direction=direction
  self.source=source
  self.angle=angle/180*np.pi
  self.seeds=seeds
  # 根据输入的网格确定距离
  self.grid=None
  self.distance=None
  # 生成光源种子
  self.nlights=None
  self.lights=None
  self.seed_polydata=None
  @staticmethod
  def mag(start,end):
  s=(start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2 + (start[2] - end[2]) ** 2
  return s ** 0.5
  @property
  def source_polydata(self):
  return pv.PolyData(self.source)
  @property
  def aim_radius(self):
  return np.tan(self.angle) * self.distance
  def update_grid(self,filename):
  self.grid=pv.read(filename)
  xmin,xmax,ymin,ymax,zmin,zmax=self.grid.bounds
  start=[xmin,ymin,zmin]
  end=[xmax,ymax,zmax]
  dis=self.mag(start,end)
  p=[[xmin, ymin, zmin],
  [xmin, ymin, zmax],
  [xmin, ymax, zmin],
  [xmin, ymax, zmax],
  [xmax, ymin, zmin],
  [xmax, ymin, zmax],
  [xmax, ymax, zmin],
  [xmax, ymax, zmax]
  ]
  for i in p:
  self.distance=max(dis, self.mag(self.source, i))
  self.distance=self.distance*1.05
  def update_lights(self):
  # 生成面
  # 终点中心
  end_center=[self.source[0] + self.distance * self.direction[0],
  self.source[1] + self.distance * self.direction[1],
  self.source[2] + self.distance * self.direction[2]
  ]
  plane=pv.Plane(center=end_center, direction=self.direction,
  i_size=2 * self.aim_radius, j_size=2 * self.aim_radius,
  i_resolution=self.seeds, j_resolution=self.seeds)
  # 定义终点
  stops=[]
  for point in plane.points:
  x1=point[0]
  y1=point[1]
  z1=point[2]
  if self.mag(point, end_center) < self.aim_radius:
  stops.append([x1, y1, z1])
  self.nlights=len(stops)
  self.lights=stops
  self.seed_polydata=pv.PolyData(stops)
  def update_shadow(self):
  shadow_cell_ids=[]
  for i in range(self.nlights):
  point, ind=self.grid.ray_trace(self.source, self.lights[i])
  if len(point) < 1:
  pass
  else:
  a=self.mag(self.source, point[0])
  end=ind[0]
  for i in point:
  if self.mag(self.source, i) < a:
  a=self.mag(self.source, i)
  end=ind
  else:
  end=ind[0]
  shadow_cell_ids.append(end)
  # 创建shadow cell属性
  shadow=np.zeros((self.grid.n_cells,))
  for i in shadow_cell_ids:
  shadow[i]=10
  self.grid.cell_arrays['shadow']=shadow
  def update(self,filename):
  self.update_grid(filename)
  self.update_lights()
  self.update_shadow()
  if __name__=='__main__':
  source=[3000,0,-2000]
  angle=20
  direction=[-0.3,0.1,1]
  seeds=100
  light=Lighting(source,angle,direction,seeds)
  light.update('car.stl')
  p=pv.Plotter()
  p.add_background_image('road.jpg')
  #
  p.add_mesh(light.source_polydata, color="yellow", point_size=20, render_points_as_spheres=True)
  p.add_mesh(light.seed_polydata, color="blue", line_width=5, label="Ray Segment")
  p.add_mesh(light.grid, scalars='shadow')
  # p.add_legend()
  p()

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档