Java 不完全光圈
我已经做了一个照明引擎,它允许阴影。它在网格系统中工作,其中每个像素都有一个光值作为整数存储在数组中。下面是它的外观演示: 阴影和实际像素着色效果良好。唯一的问题是圆圈中更远的未发光像素,出于某种原因,这形成了一个非常有趣的图案(您可能需要放大图像才能看到它)。下面是绘制灯光的代码Java 不完全光圈,java,game-engine,lighting,Java,Game Engine,Lighting,我已经做了一个照明引擎,它允许阴影。它在网格系统中工作,其中每个像素都有一个光值作为整数存储在数组中。下面是它的外观演示: 阴影和实际像素着色效果良好。唯一的问题是圆圈中更远的未发光像素,出于某种原因,这形成了一个非常有趣的图案(您可能需要放大图像才能看到它)。下面是绘制灯光的代码 public void implementLighting(){ lightLevels = new int[Game.WIDTH*Game.HEIGHT]; //Resets the light
public void implementLighting(){
lightLevels = new int[Game.WIDTH*Game.HEIGHT];
//Resets the light level map to replace it with the new lighting
for(LightSource lightSource : lights) {
//Iterates through all light sources in the world
double circumference = (Math.PI * lightSource.getRadius() * 2),
segmentToDegrees = 360 / circumference, distanceToLighting = lightSource.getLightLevel() / lightSource.getRadius();
//Degrades in brightness further out
for (double i = 0; i < circumference; i++) {
//Draws a ray to every outer pixel of the light source's reach
double radians = Math.toRadians(i*segmentToDegrees),
sine = Math.sin(radians),
cosine = Math.cos(radians),
x = lightSource.getVector().getScrX() + cosine,
y = lightSource.getVector().getScrY() + sine,
nextLit = 0;
for (double j = 0; j < lightSource.getRadius(); j++) {
int lighting = (int)(distanceToLighting * (lightSource.getRadius() - j));
double pixelHeight = super.getPixelHeight((int) x, (int)y);
if((int)j==(int)nextLit) addLighting((int)x, (int)y, lighting);
//If light is projected to have hit the pixel
if(pixelHeight > 0) {
double slope = (lightSource.getEmittingHeight() - pixelHeight) / (0 - j);
nextLit = (-lightSource.getRadius()) / slope;
/*If something is blocking it
* Using heightmap and emitting height, project where next lit pixel will be
*/
}
else nextLit++;
//Advances the light by one pixel if nothing is blocking it
x += cosine;
y += sine;
}
}
}
lights = new ArrayList<>();
}
公共照明(){
lightLevels=新智力[游戏宽度*游戏高度];
//重置灯光级别贴图以将其替换为新照明
用于(光源光源:光源){
//迭代世界上的所有光源
双圆周=(Math.PI*lightSource.getRadius()*2),
segmentToDegrees=360/圆周,distanceToLighting=lightSource.getLightLevel()/lightSource.getRadius();
//亮度进一步降低
对于(双i=0;i<周长;i++){
//将光线绘制到光源范围的每个外部像素
双弧度=数学环面(i*分段到角度),
正弦=数学正弦(弧度),
余弦=数学余弦(弧度),
x=光源.getVector().getScrX()+余弦,
y=光源.getVector().getScrY()+正弦,
nextLit=0;
对于(双j=0;j0){
双斜率=(lightSource.getEmittingHeight()-pixelHeight)/(0-j);
nextLit=(-lightSource.getRadius())/slope;
/*如果有什么东西挡住了它
*使用heightmap和发射高度,投影下一个发光像素的位置
*/
}
else-nextLit++;
//如果没有任何东西阻挡灯光,则将灯光提前一个像素
x+=余弦;
y+=正弦;
}
}
}
lights=新的ArrayList();
}
我使用的算法应该考虑光源半径内没有被物体阻挡的每个像素,所以我不确定为什么缺少一些外部像素。
谢谢
编辑:我发现,光源半径范围内的未发光像素实际上比其他像素更暗。这是addLighting方法的结果,它不仅改变像素的照明,而且将其添加到已经存在的值中。这意味着“未点亮”是只添加一次的。
为了验证这一假设,我制作了一个程序,用与生成照明相同的方式画一个圆。下面是绘制圆的代码:
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
double radius = 100,
x = (WIDTH-radius)/2,
y = (HEIGHT-radius)/2,
circumference = Math.PI*2*radius,
segmentToRadians = (360*Math.PI)/(circumference*180);
for(double i = 0; i < circumference; i++){
double radians = segmentToRadians*i,
cosine = Math.cos(radians),
sine = Math.sin(radians),
xPos = x + cosine,
yPos = y + sine;
for (int j = 0; j < radius; j++) {
if(xPos >= 0 && xPos < WIDTH && yPos >= 0 && yPos < HEIGHT) {
int rgb = image.getRGB((int) Math.round(xPos), (int) Math.round(yPos));
if (rgb == Color.white.getRGB()) image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), 0);
else image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), Color.red.getRGB());
}
xPos += cosine;
yPos += sine;
}
}
buffereImage image=新的buffereImage(宽度、高度、,
BuffereImage.TYPE_INT_RGB);
Graphics g=image.getGraphics();
g、 setColor(Color.white);
g、 fillRect(0,0,宽度,高度);
双半径=100,
x=(宽度-半径)/2,
y=(高度-半径)/2,
周长=数学PI*2*半径,
节段半径=(360*Math.PI)/(周长*180);
对于(双i=0;i<周长;i++){
双弧度=分段半径*i,
余弦=数学余弦(弧度),
正弦=数学正弦(弧度),
xPos=x+余弦,
yPos=y+sine;
对于(int j=0;j=0&&xPos=0&&yPos
结果如下:
白色像素是非彩色像素
黑色像素是一次着色的像素
红色像素是着色2次或以上的像素
所以它实际上比我最初提议的还要糟糕。这是未点亮像素和多次点亮像素的组合。这可以通过以下方法解决
由于推送浮点坐标信息并对其进行压缩,因此会发生一些有损采样
double x,y ------(snap)---> lightLevels[int ?][int ?]
要完全解决这个问题,您必须以正确的光强度在该线周围绘制透明像素(即照明度较低的像素)。但这很难计算。(见附件)
变通办法
一种更简单(但不干净)的方法是在绘制的线上绘制另一条透明较粗的线,并根据需要调整强度
或者只是让你的线条更粗,即使用更大的模糊点来补偿光线。它应该使故障不那么明显。
(参见第页的算法) 更好的方法是更改绘图方法。
手工绘制每一行都非常昂贵。
您可以使用2D精灵绘制一个圆。
但是,如果您确实希望光线投射如下图所示,则此选项不适用: 分割图形-游戏性 为了获得最佳性能和外观,您可能更喜欢使用GPU进行渲染,但使用更粗糙的算法进行光线投射以实现游戏性 尽管如此,这是一个非常复杂的话题。(例如) 参考文献 以下是更多信息:
- (带图像的opengl antialias)
- (关于带图像的directx11的相关问题)
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
double distance = Math.hypot(x - xCenter, y - yCenter);
if(distance <= radius) {
image.setRGB(x, y, YOUR_CODE_HERE);
}
}
}