...

четверг, 3 октября 2013 г.

Алгоритм трехточечного градиента

Приветствую, друзья.

В данной статье я расскажу вам о том, как рисовать треугольный градиент. Наверняка некоторые языки программирования имеют встроенные средства для выполнения этой задачи. Мы же с вами попробуем реализовать такой градиент используя стандартные геометрический формулы. Вот что в итоге необходимо получить:


Что имеем:


Рассмотрим треугольник с вершинами в точках A,B и C. Точки пересечения перпендикуляров, опущенных из вершин ABC, с противоположными сторонами треугольника назовем A", B" и C":


Задача:


Необходимо в каждой точке внутри треугольника(назовем такую точку O), определить проценты близости этой точки к каждой вершине(A,B,C) треугольника. Проценты в сумме должны дать значение 100.


Решение:


Все что нам понадобится, это формулы пересечения прямых и уравнение пермендикуляра, опущенного из точки на прямую на плоскости. Определить точку пересечения прямых можно по формуле:

// прямые, заданные по двум точкам
l1 = {p1,p2};
l2 = {p1,p2};
//
d = (l2.p2.y-l2.p1.y) * (l1.p2.x-l1.p1._x) - (l2.p2.x-l2.p1.x) * (l1.p2.y-l1.p1.y);
a = (l2.p2.x-l2.p1.x) * (l1.p1.y-l2.p1._y) - (l2.p2.y-l2.p1.y) * (l1.p1.x-l2.p1.x);
// точка пересечения
x0 = l1.p1.x + a * (l1.p2.x-l1.p1.x)/d;
y0 = l1.p1.y + a * (l1.p2.y-l1.p1.y)/d;


Получить уравнение перпендикуляра опущенного из точки A на прямую line можно так:



// точка задана по двум координатам
A = {x,y};
// линия задана по двум точкам
line = {p1,p2};
//
// определяем вектор направления линии line
dir = {0,0};
dir.x = line.p2.x - line.p1.x;
dir.y = line.p2.y - line.p1.y;
//
// очевидно, что первая точка искомой линии(перпендикуляра) будет точка A
// найдем вторую точку
point2 = {0,0};
if (dir.y != 0) {
temp = dir.x*A.x + dir.y*A.y;
point2.x = A.x != 1 ? 1 : 2;
point2.y = (-dir.x * point2.x + temp) / dir.y;
} else {
point2.x = A.x;
point2.y = line.p1.y;
}
//
// искомая линия, перпендикуляр опущенный из точки A на прямую line,
// будет прямая проходящая через точки A и point2
// Для решения поставленной в статье задачи,
// нам будет необходимо найти длину высоты,
// опущенной из вершины треугольника на противоположную сторону.
// Эту длину можно определить следующим образом.
// Находим точку пересечения прямой A-point2 и противоположной точке А прямой в треугольнике,
// и находим расстояние между найденной точкой и заданной A.


Опустим перепендикуляр из заданной точки O на пермендикуляр опущенный из точки A на противоположную сторону треугольника. Точку пересечения полученной линии с перпендикуляром, опущенным из точки A, назовем Ah. Аналогично находим точки Bh и Ch:


Процент близости от заданной точки О к вершине A можно определить отношением AAh/AA", где AAh — длина отрезка A->Ah, а AA" — длина высоты опущенной из вершины A на противоположную сторону (т.е. отрезок A->A"):



p1 = AAh/AA";
p2 = BBh/BB";
p3 = CCh/CC";


Определив проценты близости точки, можем определить цвет этой точки по формуле:



red = color1.red*p1 + color2.red*p2 + color3.red*p3;
green = color1.green*p1 + color2.green*p2 + color3.green*p3;
blue = color1.blue*p1 + color2.blue*p2 + color3.blue*p3;


Где red, green и blue — это каналы в цветовой схеме RGB. Получив значения каналов RGB для заданной точки, мы можем получить значение цвета в шеснадцатеричном формате:



color = (red << 16) + (green << 8) + blue;


Обратное преобразование из шестандцатеричного кода в RGB можно сделать с помощью формул:



color = 0xffff00;
// красный канал
red = (color >>> 16) & 0xff
// зеленый канал
green = (color >>> 8) & 0xff
// синий канал
blue = color & 0xff




Более подробно про работу с каналами RGB можно почитать здесь.

Пример на языке ActionScript 3.0




Посмотреть результат вы можете во флешке.

Исходный код флешки


package
{
import flash.display.Sprite;
import flash.filters.GlowFilter;

import ru.flashpress.callback.ICallback;
import ru.flashpress.geom.line.FPGLine2d;
import ru.flashpress.geom.line.math.FPGLineToPoint2dMath;
import ru.flashpress.geom.point.FPGPoint2d;
import ru.flashpress.geom.point.FPGPoint2dMath;
import ru.flashpress.geom.triangles.FPGTriangle2d;
import ru.flashpress.geom.view.core.FillData;
import ru.flashpress.geom.view.line.LineView;
import ru.flashpress.geom.view.point.PointView;
import ru.flashpress.geom.view.triangle.TriangleView;

public class testPointG3 extends Sprite implements ICallback
{
// геометрия треугольника
private var triangle:FPGTriangle2d;
// отображение треугольника
private var triangleView:TriangleView;
// геометория проверяемой точки
private var targetPoint:FPGPoint2d;
// отображение проверяемой точки
private var targetPointView:PointView;
//
// проекции трех вершин на противповоложные стороны
private var pHeight1:PointView;
private var pHeight2:PointView;
private var pHeight3:PointView;
//
// проекции текущей точки на высоты
// опущенные из вершин треугольника
private var crossHeight1:PointView;
private var crossHeight2:PointView;
private var crossHeight3:PointView;
//
// пукнтирные линии соединяющие текущую точку
// с проекциями crossHeight(1/2/3)
private var lineCross1:LineView;
private var lineCross2:LineView;
private var lineCross3:LineView;
//
public function testPointG3()
{
var glow:GlowFilter = new GlowFilter(0xffffff, 1, 2, 2, 10, 3);
//
var size:Number = Math.min(stage.stageWidth, stage.stageHeight)*0.8;
var p1:FPGPoint2d = new FPGPoint2d(size/2, 0);
var p2:FPGPoint2d = new FPGPoint2d(size, size*0.8);
var p3:FPGPoint2d = new FPGPoint2d(0, size*0.8);
triangle = new FPGTriangle2d(p1, p2, p3);
triangle.translate((stage.stageWidth-size)*0.5, (stage.stageHeight-size*0.8)*0.35);
//
triangleView = new TriangleView(triangle, new FillData(0x666666, 3));
triangleView.visibleHeights = true;
triangleView.heightLines.fill = new FillData(0x9900, 2);
//
triangleView.point1.filters = [glow];
triangleView.point2.filters = [glow];
triangleView.point3.filters = [glow];
triangleView.point1.fill = color1.fill(2);
triangleView.point2.fill = color2.fill(2);
triangleView.point3.fill = color3.fill(2);
triangleView.point1.name = 'A';
triangleView.point2.name = 'B';
triangleView.point3.name = 'C';
triangleView.point1.scale = 1.5;
triangleView.point2.scale = 1.5;
triangleView.point3.scale = 1.5;
//
triangleView.heightLines.line1.fill = color1.fill(2);
triangleView.heightLines.line2.fill = color2.fill(2);
triangleView.heightLines.line3.fill = color3.fill(2);
//
//
pHeight1 = new PointView(null, color1.fill());
pHeight1.enabled = false;
pHeight1.filters = [glow];
pHeight1.name = 'A"';
//
pHeight2 = new PointView(null, color2.fill());
pHeight2.enabled = false;
pHeight2.filters = [glow];
pHeight2.name = 'B"';
//
pHeight3 = new PointView(null, color3.fill());
pHeight3.enabled = false;
pHeight3.filters = [glow];
pHeight3.name = 'C"';
//
crossHeight1 = new PointView(null, color1.fill());
crossHeight1.enabled = false;
crossHeight1.filters = [glow];
crossHeight1.name = 'Ah';
//
crossHeight2 = new PointView(null, color2.fill());
crossHeight2.enabled = false;
crossHeight2.filters = [glow];
crossHeight2.name = 'Bh';
//
crossHeight3 = new PointView(null, color3.fill());
crossHeight3.enabled = false;
crossHeight3.filters = [glow];
crossHeight3.name = 'Ch';
//
lineCross1 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color1.fill(1, true));
lineCross2 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color2.fill(1, true));
lineCross3 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color3.fill(1, true));
//
var xp:Number = p3.x + (triangle.p2.x-p3.x)*0.40;
var yp:Number = p1.y + (triangle.p2.y-p1.y)*0.92;
targetPoint = new FPGPoint2d(xp, yp);
targetPointView = new PointView(targetPoint, new FillData(0x0, 1));
targetPointView.filters = [glow]
targetPointView.scale = 1.8;
//
this.addChild(triangleView);
this.addChild(pHeight1);
this.addChild(pHeight2);
this.addChild(pHeight3);
this.addChild(crossHeight1);
this.addChild(crossHeight2);
this.addChild(crossHeight3);
this.addChild(lineCross1);
this.addChild(lineCross2);
this.addChild(lineCross3);
this.addChild(targetPointView);
//
targetPoint.addCallback(this);
triangle.addCallback(this);
callbackEvent(null, null);
}

/**
* Были изменены положения вершин треугольника,
* или текущей точки targetPoint
*/
public function callbackEvent(target:Object, data:Object):void
{
pHeight1.data = triangle.heightA.p2;
pHeight2.data = triangle.heightB.p2;
pHeight3.data = triangle.heightC.p2;
//
var color:Number = getColor(targetPoint.x, targetPoint.y);
targetPointView.fill = new FillData(color);
}


private var color1:RGB = new RGB(255, 0, 0);
private var color2:RGB = new RGB(0, 200, 0);
private var color3:RGB = new RGB(0, 0, 255);
private function getColor(_x:Number, _y:Number):uint
{
var point:FPGPoint2d = new FPGPoint2d(_x, _y);
//
// находим уравнение высоты опущенной из текущей точки(point)
// на высоту опущенную из вершины A(triangle.heightA)
var h2mA:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightA);
// аналогично находим высоты для других точек B и C
var h2mB:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightB);
var h2mC:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightC);
if (!h2mA || !h2mB|| !h2mC) return 0xffffff;
//
// h2mA.p2 - это точка пересечения прямой h2mA с
// высотой опущенной из вершины A
crossHeight1.data = h2mA.p2;
crossHeight2.data = h2mB.p2;
crossHeight3.data = h2mC.p2;
//
//
// находим расстояние от текущей вершины треугольника
// до точки пересечения прямой h2mA c высотой, опущенной из вершины A
var h2Alen:Number = FPGPoint2dMath.length(triangle.p1, h2mA.p2);
// аналогично находим расстояния для других точек
var h2Blen:Number = FPGPoint2dMath.length(triangle.p2, h2mB.p2);
var h2Clen:Number = FPGPoint2dMath.length(triangle.p3, h2mC.p2);
//
// рисуем пунктриную линию
lineCross1.data = h2mA;
lineCross2.data = h2mB;
lineCross3.data = h2mC;
//
// находим проценты близости
var p1:Number = 1-h2Alen/(triangle.heightA.length);
var p2:Number = 1-h2Blen/(triangle.heightB.length);
var p3:Number = 1-h2Clen/(triangle.heightC.length);
//
// задаем нижнюю границу
if (p1 < 0) p1 = 0;
if (p2 < 0) p2 = 0;
if (p3 < 0) p3 = 0;
//
//
// вычисляем значения цветовых каналов red, green и blue
var r:Number = color1.r*p1 + color2.r*p2 + color3.r*p3;
var g:Number = color1.g*p1 + color2.g*p2 + color3.g*p3;
var b:Number = color1.b*p1 + color2.b*p2 + color3.b*p3;
// задаем верхнюю границу
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
// вычисляем значение цвета
var color:int = (r << 16) + (g << 8) + b;
return color;
}
}
}
import ru.flashpress.geom.view.core.FillData;



class RGB
{
public var r:uint;
public var g:uint;
public var b:uint;
public var code:uint;
public function RGB(r:uint, g:uint, b:uint)
{
this.r = r;
this.g = g;
this.b = b;
this.code = (r << 16) + (g << 8) + b;
}

public function fill(stroke:uint=1, dotline:Boolean=false):FillData
{
return new FillData(this.code, stroke, dotline);
}
}







Для геометрических вычислений используется библиотека FPGeometry2d.swc.

Пример ActionScript3.0 с использованием шейдера




Результат закраски треугольника с использованием шейдера во флешке.

Главный класс флешки testShaderG3.as


package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;

import ru.flashpress.filters.G3Shader;

public class testShaderG3 extends Sprite
{
// шейдер, рисующий треугольный градиент
private var shader:G3Shader;
public function testShaderG3()
{
start();
}

private var p1:Sprite;
private var p2:Sprite;
private var p3:Sprite;
private function start():void
{
shader = new G3Shader();
//
// создаем три вершины треугольника
p1 = createPoint(200, 50);
p2 = createPoint(350, 350);
p3 = createPoint(50, 350);
//
shader.initPoints( new Point(p1.x, p1.y),
new Point(p2.x, p2.y),
new Point(p3.x, p3.y));
//
redraw();
}
private function createPoint(_x:Number, _y:Number):Sprite
{
var point:Sprite = new Sprite();
point.graphics.beginFill(0x0, 0.5);
point.graphics.drawCircle(0, 0, 10);
this.addChild(point);
point.x = _x;
point.y = _y;
point.buttonMode = true;
point.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
return point;
}
private var currentPoint:Sprite;
private function downHandler(event:MouseEvent):void
{
currentPoint = event.target as Sprite;
currentPoint.startDrag();
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, moveHandler);
this.stage.addEventListener(MouseEvent.MOUSE_UP, upHandler);
}
private function moveHandler(event:MouseEvent):void
{
event.updateAfterEvent();
switch (currentPoint) {
// задаем шейдеру значения вершин треугольника
case p1:
shader.point1 = new Point(p1.x, p1.y);
break;
case p2:
shader.point2 = new Point(p2.x, p2.y);
break;
case p3:
shader.point3 = new Point(p3.x, p3.y);
break;
}
redraw();
}
private function upHandler(event:MouseEvent):void
{
currentPoint.stopDrag();
this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveHandler);
this.stage.removeEventListener(MouseEvent.MOUSE_UP, upHandler);
}

private function redraw():void
{
this.graphics.clear();
this.graphics.beginFill(0xff0000, 1);
this.graphics.beginShaderFill(shader);
this.graphics.moveTo(p1.x, p1.y);
this.graphics.lineTo(p2.x, p2.y);
this.graphics.lineTo(p3.x, p3.y);
}
}
}







Класс G3Shader.as


package ru.flashpress.filters
{
import flash.display.Shader;
import flash.geom.Point;

/**
* Шейдер, рисующий треугольный градиент, по заданным точкам и цветам к ним
* @author Serious Sam
*/
public class G3Shader extends Shader
{
private static var dir:Point = new Point();
private static var point2:Point = new Point();
private static var temp:Number;
private static var k1:Number;
private static var k2:Number;
private static function getHeight(o:Point, p1:Point, p2:Point):void
{
dir.x = p2.x-p1.x;
dir.y = p2.y-p1.y;
point2.x = 0;
point2.y = 0;
if (dir.y != 0) {
temp = dir.x*o.x + dir.y*o.y;
point2.x = o.x != 1 ? 1 : 2;
point2.y = (-dir.x * point2.x + temp) / dir.y;
} else {
point2.x = o.x;
point2.y = p1.y;
}
//
k1 = (point2.y-o.y)*(p2.x-p1.x) - (point2.x-o.x)*(p2.y-p1.y);
k2 = (point2.x-o.x)*(p1.y-o.y) - (point2.y-o.y)*(p1.x-o.x);
pointH.x = p1.x + k2*(p2.x-p1.x)/k1;
pointH.y = p1.y + k2*(p2.y-p1.y)/k1;
}
//
//
//
[Embed(source="g3.pbj", mimeType="application/octet-stream")]
private var BytecodesClass:Class;
//
/**
* @private
*/
public function G3Shader()
{
super(new BytecodesClass());
}

private static var pointH:Point = new Point();
/**
* В этом методе необходимо сообщить шейдеру значения координат вершин (a, b и c)
* а так же координаты проекций этих вершин на противоположные стороны (ah, bh, ch)
*/
private function reinit():void
{
// вершина A треугольника
this.data.a.value = [_point1.x, _point1.y];
getHeight(_point1, _point2, _point3);
// проекция вершины A на сторону BC
this.data.ah.value = [pointH.x, pointH.y];
//
this.data.b.value = [_point2.x, _point2.y];
getHeight(_point2, _point3, _point1);
this.data.bh.value = [pointH.x, pointH.y];
//
this.data.c.value = [_point3.x, _point3.y];
getHeight(_point3, _point1, _point2);
this.data.ch.value = [pointH.x, pointH.y];
}

/**
* Инициализировать координаты вершин трегольника
*/
public function initPoints(p1:Point, p2:Point, p3:Point):void
{
this._point1.x = p1.x;
this._point1.y = p1.y;
//
this._point2.x = p2.x;
this._point2.y = p2.y;
//
this._point3.x = p3.x;
this._point3.y = p3.y;
//
reinit();
}

private var _point1:Point = new Point(100, 0);
/**
* Координаты первой вершины треугольника
*/
public function get point1():Point {return this._point1.clone();}
public function set point1(value:Point):void
{
this._point1.x = value.x;
this._point1.y = value.y;
//
this.reinit();
}

private var _point2:Point = new Point(200, 200);
/**
* Координаты второй вершины треугольника
*/
public function get point2():Point {return this._point2.clone();}
public function set point2(value:Point):void
{
this._point2.x = value.x;
this._point2.y = value.y;
//
this.reinit();
}

private var _point3:Point = new Point(0, 200);
/**
* Координаты третьей вершины треугольника
*/
public function get point3():Point {return this._point3.clone();}
public function set point3(value:Point):void
{
this._point3.x = value.x;
this._point3.y = value.y;
//
this.reinit();
}

/**
* Инициализировать цвета вершин треугольника
*/
public function initColors(color1:uint, color2:uint, color3:uint):void
{
this.color1 = color1;
this.color2 = color2;
this.color3 = color3;
}

private var _color1:Number = 0;
/**
* Цвет первой вершины треугольника
*/
public function get color1():uint {return this._color1;}
public function set color1(value:uint):void
{
this._color1 = value;
this.data.color1.value = [ ((value >>> 16) & 0xff)/255,
((value >>> 8) & 0xff)/255,
(value & 0xff)/255];
}

private var _color2:Number = 0;
/**
* Цвет второй вершины треугольника
*/
public function get color2():uint {return this._color2;}
public function set color2(value:uint):void
{
this._color2 = value;
this.data.color2.value = [ ((value >>> 16) & 0xff)/255,
((value >>> 8) & 0xff)/255,
(value & 0xff)/255];
}

private var _color3:Number = 0;
/**
* Цвет третьей вершины треугольника
*/
public function get color3():uint {return this._color3;}
public function set color3(value:uint):void
{
this._color3 = value;
this.data.color3.value = [ ((value >>> 16) & 0xff)/255,
((value >>> 8) & 0xff)/255,
(value & 0xff)/255];
}
}
}







Шейдер g3.pbk


<languageVersion: 1.0;>

kernel TriangleGradient
< namespace : "flashpress.ru";
vendor : "FlashPress.ru";
version : 1;
description : "Triangle Gradient"; >
{

parameter float2 a
<
defaultValue:float2(50.0, 50.0);
>;
parameter float2 ah
<
defaultValue:float2(50.0, 200.0);
>;
parameter float2 b
<
defaultValue:float2(50.0, 200.0);
>;
parameter float2 bh
<
defaultValue:float2(125.0, 125.0);
>;
parameter float2 c
<
defaultValue:float2(200.0, 200.0);
>;
parameter float2 ch
<
defaultValue:float2(50.0, 200.0);
>;
parameter float3 color1
<
minValue:float3(0.0, 0.0, 0.0);
maxValue:float3(1.0, 1.0, 1.0);
defaultValue:float3(1.0, 0.0, 0.0);
>;
parameter float3 color2
<
minValue:float3(0.0, 0.0, 0.0);
maxValue:float3(1.0, 1.0, 1.0);
defaultValue:float3(0.0, 1.0, 0.0);
>;
parameter float3 color3
<
minValue:float3(0.0, 0.0, 0.0);
maxValue:float3(1.0, 1.0, 1.0);
defaultValue:float3(0.0, 0.0, 1.0);
>;

input image4 src;
output float4 dst;

void
evaluatePixel()
{
//
//
//
//
float2 direction;
float lineA;
float lineB;
float lineC;
float checkd;
float checka;
float2 point;
float2 o = outCoord();
//
// уравнение перпендикуляра опущенного из точки O на прямую a-ah
direction = ah-a;
lineA = -direction.y;
lineB = -direction.x;
lineC = direction.x*o.x + direction.y*o.y;
if (lineA != 0.0) {
point = float2(o.x + 1.0, 0);
point.y = -(lineB*point.x+lineC)/lineA;
} else {
point = float2(o.x, a.y);
}
// пересечение p1-point и b-c
checkd = (point.y-o.y)*(ah.x-a.x) - (point.x-o.x)*(ah.y-a.y);
checka = (point.x-o.x)*(a.y-o.y) - (point.y-o.y)*(a.x-o.x);
// точка пересечения перпендикуляра опущенного из A и прямой BC
float2 aho = float2(0.0, 0.0);
aho.x = a.x + checka*(ah.x-a.x)/checkd;
aho.y = a.y + checka*(ah.y-a.y)/checkd;
//
//
//
// уравнение перпендикуляра опущенного из точки O на прямую b-bh
direction = bh-b;
lineA = -direction.y;
lineB = -direction.x;
lineC = direction.x*o.x + direction.y*o.y;
if (lineA != 0.0) {
point = float2(o.x+1.0, 0.0);
point.y = -(lineB*point.x+lineC)/lineA;
} else {
point = float2(o.x, b.y);
}
// пересечение p1-point и b-c
checkd = (point.y-o.y)*(bh.x-b.x) - (point.x-o.x)*(bh.y-b.y);
checka = (point.x-o.x)*(b.y-o.y) - (point.y-o.y)*(b.x-o.x);
// точка пересечения перпендикуляра опущенного из A и прямой BC
float2 bho = float2(0.0, 0.0);
bho.x = b.x + checka*(bh.x-b.x)/checkd;
bho.y = b.y + checka*(bh.y-b.y)/checkd;
//
//
//
// уравнение перпендикуляра опущенного из точки O на прямую c-ch
direction = ch-c;
lineA = -direction.y;
lineB = -direction.x;
lineC = direction.x*o.x + direction.y*o.y;
if (lineA != 0.0) {
point = float2(o.x+1.0, 0.0);
point.y = -(lineB*point.x+lineC)/lineA;
} else {
point = float2(o.x, c.y);
}
// пересечение p1-point и b-c
checkd = (point.y-o.y)*(ch.x-c.x) - (point.x-o.x)*(ch.y-c.y);
checka = (point.x-o.x)*(c.y-o.y) - (point.y-o.y)*(c.x-o.x);
// точка пересечения перпендикуляра опущенного из A и прямой BC
float2 cho = float2(0.0, 0.0);
cho.x = c.x + checka*(ch.x-c.x)/checkd;
cho.y = c.y + checka*(ch.y-c.y)/checkd;
//
//
//
float pp1 = 1.0-((a.x-aho.x)*(a.x-aho.x)+(a.y-aho.y)*(a.y-aho.y))/((a.x-ah.x)*(a.x-ah.x)+(a.y-ah.y)*(a.y-ah.y));
float pp2 = 1.0-((b.x-bho.x)*(b.x-bho.x)+(b.y-bho.y)*(b.y-bho.y))/((b.x-bh.x)*(b.x-bh.x)+(b.y-bh.y)*(b.y-bh.y));
float pp3 = 1.0-((c.x-cho.x)*(c.x-cho.x)+(c.y-cho.y)*(c.y-cho.y))/((c.x-ch.x)*(c.x-ch.x)+(c.y-ch.y)*(c.y-ch.y));
//
dst = float4(0.0, 0.0, 0.0, 1.0);
dst.r = color1.r*pp1 + color2.r*pp2 + color3.r*pp3;
dst.g = color1.g*pp1 + color2.g*pp2 + color3.g*pp3;
dst.b = color1.b*pp1 + color2.b*pp2 + color3.b*pp3;
}
}





This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий