Làm thế nào để bạn tạo ra tiếng ồn Perlin có thể điều chỉnh?


127

Liên quan:

Tôi muốn tạo ra tiếng ồn Perlin có thể điều chỉnh được. Tôi đang làm việc từ các PerlinNoise*() chức năng của Paul Bourke , giống như thế này:

// alpha is the "division factor" (how much to damp subsequent octaves with (usually 2))
// beta is the factor that multiplies your "jump" into the noise (usually 2)
// n is the number of "octaves" to add in
double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[2],scale = 1;

   p[0] = x;
   p[1] = y;
   for (i=0;i<n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
   }
   return(sum);
}

Sử dụng mã như:

real val = PerlinNoise2D( x,y, 2, 2, 12 ) ; // test

return val*val*skyColor + 2*val*(1-val)*gray + (1-val)*(1-val)*cloudColor ;

Cho bầu trời như

không thể phá hủy

Điều này không thể thay đổi.

Các giá trị pixel là 0-> 256 (chiều rộng và chiều cao) và pixel (0,0) sử dụng (x, y) = (0,0) và pixel (256,256) sử dụng (x, y) = (1,1)

Làm thế nào tôi có thể làm cho nó dễ dàng?


14
Chỉ cần FYI, những gì bạn có không có tiếng ồn Perlin; đó là tiếng ồn fractal. Tiếng ồn Perlin có khả năng là hàm "noise2" tạo ra mỗi quãng tám của tiếng ồn fractal.
Nathan Reed

Câu trả lời:


80

Có hai phần để tạo ra tiếng ồn fBm có thể điều chỉnh liền mạch như thế này. Trước tiên, bạn cần làm cho chức năng tiếng ồn Perlin có thể điều chỉnh được. Đây là một số mã Python cho một hàm nhiễu Perlin đơn giản hoạt động với bất kỳ khoảng thời gian nào lên tới 256 (bạn có thể mở rộng nó một cách tầm thường bao nhiêu tùy thích bằng cách sửa đổi phần đầu tiên):

import random
import math
from PIL import Image

perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
         math.sin(a * 2.0 * math.pi / 256))
         for a in range(256)]

def noise(x, y, per):
    def surflet(gridX, gridY):
        distX, distY = abs(x-gridX), abs(y-gridY)
        polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
        polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
        hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
        grad = (x-gridX)*dirs[hashed][0] + (y-gridY)*dirs[hashed][1]
        return polyX * polyY * grad
    intX, intY = int(x), int(y)
    return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
            surflet(intX+0, intY+1) + surflet(intX+1, intY+1))

Tiếng ồn Perlin được tạo ra từ một tập hợp các "phụ" nhỏ, là sản phẩm của một gradient được định hướng ngẫu nhiên và một hàm dự phòng đa thức tách rời. Điều này mang lại một vùng dương (màu vàng) và vùng âm (màu xanh)

Hạt nhân

Các phần tử có phạm vi 2x2 và được tập trung vào các điểm mạng nguyên, do đó, giá trị của nhiễu Perlin tại mỗi điểm trong không gian được tạo ra bằng cách tính tổng các phần phụ ở các góc của ô mà nó chiếm.

Tổng kết

Nếu bạn thực hiện bọc hướng gradient với một khoảng thời gian, thì tiếng ồn sẽ tự ngắt hoàn toàn với cùng một khoảng thời gian. Đây là lý do tại sao đoạn mã trên lấy tọa độ mạng modulo trong khoảng thời gian trước khi băm nó thông qua bảng hoán vị.

Bước khác, là khi tính tổng quãng tám, bạn sẽ muốn chia tỷ lệ thời gian với tần số của quãng tám. Về cơ bản, bạn sẽ muốn mỗi quãng tám xếp toàn bộ hình ảnh chỉ một lần, thay vì nhiều lần:

def fBm(x, y, per, octs):
    val = 0
    for o in range(octs):
        val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
    return val

Đặt nó cùng nhau và bạn nhận được một cái gì đó như thế này:

size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
    for x in range(size):
        data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")

Tiếng ồn fBm có thể điều chỉnh

Như bạn có thể thấy, điều này thực sự gạch liền mạch:

fBm tiếng ồn, lát gạch

Với một số điều chỉnh nhỏ và ánh xạ màu, đây là hình ảnh đám mây được xếp theo tỷ lệ 2x2:

Mây!

Hi vọng điêu nay co ich!


3
Tôi không phải là một anh chàng trăn, vì vậy tôi hỏi, làm thế nào để x*2**ochuyển đổi sang C? là nó: x*pow(2,o)hay pow(x*2,o)?
idev

7
x*pow(2, o), vì lũy thừa có độ ưu tiên cao hơn phép nhân.
John Calsbeek

1
ai đó có thể chuyển đổi này thành C? Tôi có vấn đề lớn khi hiểu mã này, vì tôi chưa bao giờ làm gì với python. ví dụ agiá trị là gì? và tôi không chắc làm thế nào các chức năng chuyển đổi thành C ... tôi chỉ nhận được các đường thẳng trong đầu ra.
idev

1
Đây chắc chắn là giải pháp tốt nhất miễn là bạn ổn với miền tiếng ồn của bạn được gắn với hình dạng của ô của bạn. Ví dụ, điều này không cho phép xoay tùy ý. Nhưng nếu bạn không cần bất cứ điều gì như vậy, đây là câu trả lời lý tưởng.
John Calsbeek

1
Lưu ý: nếu bạn muốn tạo kích thước khác hơn 128, KHÔNG thay đổi các giá trị số trên dòng im.putdata(data, 128, 128). (Đối với những người không quen thuộc với python hoặc PIL: chúng có nghĩa là tỷ lệ và độ lệch, không phải kích thước hình ảnh.)
Antti Kissaniemi

87

Đây là một cách khá thông minh sử dụng tiếng ồn 4D Perlin.

Về cơ bản, ánh xạ tọa độ X của pixel của bạn thành một vòng tròn 2D và tọa độ Y của pixel của bạn thành một vòng tròn 2D thứ hai và đặt hai vòng tròn đó trực giao với nhau trong không gian 4D. Kết cấu thu được có thể điều chỉnh được, không có biến dạng rõ ràng và không lặp lại theo cách mà một kết cấu được nhân đôi.

Mã sao chép từ bài viết:

for x=0,bufferwidth-1,1 do
    for y=0,bufferheight-1,1 do
        local s=x/bufferwidth
        local t=y/bufferheight
        local dx=x2-x1
        local dy=y2-y1

        local nx=x1+cos(s*2*pi)*dx/(2*pi)
        local ny=y1+cos(t*2*pi)*dy/(2*pi)
        local nz=x1+sin(s*2*pi)*dx/(2*pi)
        local nw=y1+sin(t*2*pi)*dy/(2*pi)

        buffer:set(x,y,Noise4D(nx,ny,nz,nw))
    end
end

3
Đây chắc chắn là câu trả lời đúng. Thêm kích thước là một thủ thuật toán học cũ. Olinde Coleues docet (Sir WR Hamilton docet quá nhưng hơi ít)
FxIII

@FxIII, bạn có biết nên thực hiện phương thức Noise4D () này như thế nào không? Tôi muốn thử điều này nhưng tôi không biết làm thế nào mà Noise4D () này hoạt động.
idev

4
Bạn có thể sử dụng bất kỳ chức năng tiếng ồn 4D. Tiếng ồn Simplex sẽ là đề nghị của tôi. webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
John Calsbeek

2
cảm ơn john làm cho nó hoạt động, ngọt ngào! không ai nói điều đó, nhưng: x1, y1, x2, y2 dường như là một tỷ lệ nào đó, khoảng cách lớn hơn, tiếng ồn chi tiết. nếu điều này giúp bất cứ ai.
idev

5
Lưu ý rằng điều này tương đương về mặt cấu trúc với câu trả lời của bobobobo: ánh xạ của bạn nhúng 2 hình xuyến vào ℝ⁴, điều này có thể xảy ra mà không làm biến dạng số liệu mà bạn có thể nhận được khi nhúng nó vào.
rẽ trái

22

Ok, tôi hiểu rồi Câu trả lời là đi trong một hình xuyến trong tiếng ồn 3D, tạo ra kết cấu 2D từ nó.

hình xuyến kết thúc tốt đẹp

Mã số:

Color Sky( double x, double y, double z )
{
  // Calling PerlinNoise3( x,y,z ),
  // x, y, z _Must be_ between 0 and 1
  // for this to tile correctly
  double c=4, a=1; // torus parameters (controlling size)
  double xt = (c+a*cos(2*PI*y))*cos(2*PI*x);
  double yt = (c+a*cos(2*PI*y))*sin(2*PI*x);
  double zt = a*sin(2*PI*y);
  double val = PerlinNoise3D( xt,yt,zt, 1.5, 2, 12 ) ; // torus

  return val*val*cloudWhite + 2*val*(1-val)*gray + (1-val)*(1-val)*skyBlue ;
}

Các kết quả:

Một lần:

bầu trời

Và lát gạch:

cho thấy nó gạch


6
Nó hoạt động khá tốt, nhưng có vẻ như bạn đang bị méo mó do độ cong của hình xuyến.
Nathan Reed

1
bạn thực sự có thể chỉ cần modulo vị trí, nhưng tôi thích tất cả các câu trả lời tuyệt vời / sáng tạo cho câu hỏi này. Vì vậy, nhiều cách khác nhau để làm điều tương tự.

Tôi nhận thấy bạn thực sự không muốn sử dụng các giá trị 0-1, nhưng các giá trị 0-0.9999 ...! vì vậy bạn sẽ sử dụng: x / width, y / height, v.v ... nếu không thì các đường nối không khớp (làm cho các cạnh đối diện chính xác cùng pixel). ngoài ra, có vẻ như hàm PerlinNoir3D () cũng cần kẹp cho giá trị kết quả hoặc một số giá trị pixel bị tràn.
idev

@Nathan, bạn có biết cách sửa méo không?
idev

2
@idev Tôi tin rằng cách khắc phục sự biến dạng là sử dụng phương pháp 4D trong câu trả lời hàng đầu của câu hỏi này. ;)
Nathan Reed

16

Một cách đơn giản mà tôi có thể nghĩ đến là lấy đầu ra của hàm nhiễu và nhân bản / lật nó thành một hình ảnh có kích thước gấp đôi. Thật khó để giải thích vì vậy đây là một hình ảnh: nhập mô tả hình ảnh ở đây

Bây giờ, trong trường hợp này, nó khá rõ ràng những gì bạn đã làm khi bạn nhìn vào điều này. Tôi có thể nghĩ ra hai cách để (có thể :-)) giải quyết điều này:

  1. Bạn có thể lấy hình ảnh lớn hơn đó và sau đó tạo ra một số nhiễu hơn ở phía trên nó nhưng (và tôi không chắc liệu điều này có thể không) tập trung vào giữa (vì vậy các cạnh vẫn giữ nguyên). Nó có thể thêm một chút khác biệt sẽ khiến não bạn nghĩ rằng đó không chỉ là hình ảnh phản chiếu.

  2. (Tôi cũng không chắc chắn nếu điều này là có thể) Bạn có thể thử thay đổi các yếu tố đầu vào cho chức năng nhiễu để tạo ra hình ảnh ban đầu khác nhau. Bạn sẽ phải làm điều này bằng cách dùng thử và lỗi, nhưng hãy tìm các tính năng thu hút ánh mắt của bạn khi bạn xếp / phản chiếu nó và sau đó thử và làm cho nó không tạo ra chúng.

Hi vọng điêu nay co ich.


3
Rất đẹp nhưng quá đối xứng!
bobobobo

1
@bobobobo Đó là những gì tôi nghĩ rằng các bước khác sẽ giảm bớt. Vì vậy, bạn có thể tạo "cơ sở" bằng phương pháp này và sau đó thêm một số chi tiết khác trên toàn bộ để làm cho nó trông giống như nó không (vì vậy) được nhân đôi.
Richard Marskell - Drackir

Bạn bắt đầu nhận được một số mô hình kỳ lạ khi bạn làm điều này. Điều này đặc biệt trông giống như một con bướm. Giải pháp dễ dàng, mặc dù.
thịt

Đây cũng là cách tiếp cận đầu tiên của tôi, nhưng nó có một vấn đề, có thể nhìn thấy ở đây: dl.dropbox.com/u/6620757/noise_seam.png Khi bạn vượt qua một ranh giới lật, bạn gây ra sự khác biệt trong chức năng tiếng ồn bằng cách đảo ngược ngay lập tức độ dốc của chức năng. Ngay cả khi bạn áp dụng chức năng nhiễu thứ hai ở trên, nó vẫn có thể nhìn thấy trong đầu ra.
Jherico

Ý tưởng tuyệt vời. Điều này có thể dễ dàng thực hiện trong trình đổ bóng pixel bằng chức năng sóng tam giác :tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0))
tigrou

10

Phiên bản đầu tiên của câu trả lời này thực sự sai, tôi đã cập nhật nó

Một phương pháp tôi đã sử dụng thành công là tạo miền nhiễu . Nói cách khác, làm cho noise2()chức năng cơ sở của bạn định kỳ. Nếu noise2()là định kỳ và betalà số nguyên, kết quả là nhiễu sẽ có cùng thời gian với noise2().

Làm thế nào chúng ta có thể làm cho noise2()định kỳ? Trong hầu hết các triển khai, chức năng này sử dụng một số loại nhiễu mạng. Đó là, nó nhận được các số ngẫu nhiên ở tọa độ nguyên và nội suy chúng. Ví dụ:

function InterpolatedNoise_1D(float x)

  integer_X    = int(x)
  fractional_X = x - integer_X

  v1 = SmoothedNoise1(integer_X)
  v2 = SmoothedNoise1(integer_X + 1)

  return Interpolate(v1 , v2 , fractional_X)

end function

Hàm này có thể được sửa đổi một cách tầm thường để trở thành định kỳ với chu kỳ nguyên. Chỉ cần thêm một dòng:

integer_X = integer_X % Period

trước khi tính toán v1v2. Theo cách này, các giá trị tại tọa độ nguyên sẽ lặp lại mọi đơn vị Thời gian và phép nội suy sẽ đảm bảo rằng hàm kết quả được trơn tru.

Tuy nhiên, lưu ý rằng điều này chỉ hoạt động khi Chu kỳ lớn hơn 1. Vì vậy, để thực sự sử dụng điều này trong việc tạo họa tiết liền mạch, bạn phải lấy mẫu hình vuông Thời gian x, không phải là 1x1.


Nhưng làm thế nào để bạn thực hiện noise2định kỳ (với một khoảng thời gian ngắn như 1 đơn vị)? Tôi nghĩ đó là những gì câu hỏi cuối cùng được hỏi. Tiếng ồn Perlin tiêu chuẩn là định kỳ với chu kỳ 256 trên mỗi trục nhưng bạn muốn có tiếng ồn được sửa đổi với chu kỳ nhỏ hơn.
Nathan Reed

@Nathan Reed Nếu bạn gọi noise2như được đề xuất, bạn sẽ nhận được kết quả định kỳ, cho dù chức năng đó có định kỳ hay không. Bởi vì các đối số bao quanh mỗi 1 đơn vị.
Nevermind

1
Nhưng sau đó bạn có đường nối tại các đường lưới, phải không? Vì không có gì đảm bảo rằng noise2 (0, 0.999) là bất cứ thứ gì gần noise2 (0, 0), trừ khi tôi đã bỏ lỡ điều gì đó.
Nathan Reed

1
@Nathan Reed Đó là một điểm tốt. Trên thực tế, tôi chỉ kiểm tra lại mã cũ của mình và hóa ra tôi đã sai. Tôi sẽ chỉnh sửa câu trả lời ngay bây giờ.
Nevermind

Tuyệt quá! Đây thực sự là một câu trả lời tốt. +1 :)
Nathan Reed

6

Một cách khác là tạo tiếng ồn bằng thư viện libnoise. Bạn có thể tạo ra tiếng ồn trên một lượng không gian vô hạn theo lý thuyết, liền mạch.

Hãy xem những điều sau đây: http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile

Ngoài ra còn có một cổng XNA ở trên tại: http://bigblackblock.com/tools/libnoothyna

Nếu bạn kết thúc bằng cổng XNA, bạn có thể làm một cái gì đó như thế này:

Perlin perlin = new Perlin();
perlin.Frequency = 0.5f;                //height
perlin.Lacunarity = 2f;                 //frequency increase between octaves
perlin.OctaveCount = 5;                 //Number of passes
perlin.Persistence = 0.45f;             //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;

//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);

//Get a section
_map.GeneratePlanar(left, right, top, down);

GeneratePlanar là chức năng gọi để lấy các phần theo từng hướng sẽ kết nối liền mạch với phần còn lại của kết cấu.

Tất nhiên, phương pháp này tốn kém hơn chỉ đơn giản là có một kết cấu duy nhất có thể được sử dụng trên nhiều bề mặt. Nếu bạn đang tìm cách tạo ra một số kết cấu có thể điều chỉnh ngẫu nhiên, đây có thể là điều mà bạn quan tâm.


6

Mặc dù có một số câu trả lời ở đây sẽ hoạt động, nhưng hầu hết chúng đều phức tạp, chậm và có vấn đề.

Tất cả những gì bạn thực sự cần làm là sử dụng chức năng tạo tiếng ồn định kỳ . Đó là nó!

Một triển khai miền công cộng tuyệt vời dựa trên thuật toán tiếng ồn "nâng cao" của Perlin có thể được tìm thấy ở đây . Hàm bạn cần là pnoise2. Mã được viết bởi Stefan Gustavson, người đã đưa ra một nhận xét rõ ràng ở đây về chính xác vấn đề này, và cách những người khác đã thực hiện sai cách tiếp cận. Lắng nghe Gustavson, anh ta biết anh ta đang nói về cái gì.

Về các phép chiếu hình cầu khác nhau mà một số người ở đây đã đề xuất: tốt, về bản chất chúng hoạt động (chậm), nhưng chúng cũng tạo ra kết cấu 2D là một hình cầu dẹt, do đó các cạnh sẽ cô đọng hơn, có khả năng tạo ra hiệu ứng không mong muốn. Tất nhiên, nếu bạn có ý định cho kết cấu 2D của mình được chiếu lên một quả cầu, đó là cách để đi, nhưng đó không phải là điều được yêu cầu.


4

Đây là một cách đơn giản hơn nhiều để thực hiện tiếng ồn lát gạch:

ốp lát tiếng ồn perlin từ mã shadertoy

Bạn sử dụng một mô-đun bọc xung quanh cho mỗi quy mô của tiếng ồn. Chúng phù hợp với các cạnh của khu vực cho dù bạn sử dụng thang tần số nào. Vì vậy, bạn chỉ phải sử dụng tiếng ồn 2D bình thường nhanh hơn rất nhiều. Đây là mã WebGL trực tiếp có thể tìm thấy tại ShaderToy: https://www.shadertoy.com/view/4dlGW2

Ba hàm trên cùng thực hiện tất cả công việc và fBM được truyền một vectơ x / y trong phạm vi 0,0 đến 1,0.

// Tileable noise, for creating useful textures. By David Hoskins, Sept. 2013.
// It can be extrapolated to other types of randomised texture.

#define SHOW_TILING
#define TILES 2.0

//----------------------------------------------------------------------------------------
float Hash(in vec2 p, in float scale)
{
    // This is tiling part, adjusts with the scale...
    p = mod(p, scale);
    return fract(sin(dot(p, vec2(35.6898, 24.3563))) * 353753.373453);
}

//----------------------------------------------------------------------------------------
float Noise(in vec2 x, in float scale )
{
    x *= scale;

    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    //f = (1.0-cos(f*3.1415927)) * .5;
    float res = mix(mix(Hash(p,                  scale),
        Hash(p + vec2(1.0, 0.0), scale), f.x),
        mix(Hash(p + vec2(0.0, 1.0), scale),
        Hash(p + vec2(1.0, 1.0), scale), f.x), f.y);
    return res;
}

//----------------------------------------------------------------------------------------
float fBm(in vec2 p)
{
    float f = 0.4;
    // Change starting scale to any integer value...
    float scale = 14.0;
    float amp = 0.55;
    for (int i = 0; i < 8; i++)
    {
        f += Noise(p, scale) * amp;
        amp *= -.65;
        // Scale must be multiplied by an integer value...
        scale *= 2.0;
    }
    return f;
}

//----------------------------------------------------------------------------------------
void main(void)
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;

#ifdef SHOW_TILING
    uv *= TILES;
#endif

    // Do the noise cloud (fractal Brownian motion)
    float bri = fBm(uv);

    bri = min(bri * bri, 1.0); // ...cranked up the contrast for no reason.
    vec3 col = vec3(bri);

#ifdef SHOW_TILING
    vec2 pixel = (TILES / iResolution.xy);
    // Flash borders...
    if (uv.x > pixel.x && uv.y > pixel.y                                        // Not first pixel
    && (fract(uv.x) < pixel.x || fract(uv.y) < pixel.y) // Is it on a border?
    && mod(iGlobalTime-2.0, 4.0) < 2.0)                 // Flash every 2 seconds
    {
        col = vec3(1.0, 1.0, 0.0);
    }
#endif
    gl_FragColor = vec4(col,1.0);
}

1
Liên kết hình ảnh của bạn đã chết. Tôi đã đoán đúng nhất và thay thế nó bằng một ảnh chụp màn hình đầu ra từ mã shadertoy mà bạn đã đăng. Nếu điều đó không chính xác, vui lòng tải lại hình ảnh dự định của bạn trực tiếp lên máy chủ Stack Exchange.
Pikalek

3

Tôi đã có một số kết quả không tồi khi nội suy gần các cạnh của gạch (được bao bọc cạnh), nhưng nó phụ thuộc vào hiệu ứng bạn đang cố gắng đạt được và các thông số tiếng ồn chính xác. Hoạt động tuyệt vời cho tiếng ồn hơi mờ, không tốt với những cái tăng đột biến / hạt mịn.


0

Tôi đã kiểm tra chủ đề này để tìm câu trả lời cho một vấn đề tương tự, sau đó tôi nhận được một giải pháp gọn và gọn từ nhà phát triển mã python này để tạo ra tiếng ồn fractal từ tiếng ồn perlin / simplex. Mã cập nhật được cung cấp trong vấn đề (đóng) này và có thể được nối lại trong việc đặt độ dốc cho phía bên phải của "trình tạo" bằng với các bên ở bên trái (và tương tự cho trên và dưới), chẳng hạn như trong

# Gradients
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
# Make the noise tileable
gradients[-1,:] = gradients[0,:]
gradients[:,-1] = gradients[:,0]

Có vẻ như là một giải pháp thanh lịch và sạch sẽ, tôi tránh sao chép toàn bộ mã ở đây (vì đó không phải là giải pháp của riêng tôi), nhưng nó có sẵn tại liên kết được đưa ra ở trên. Hy vọng điều này có thể hữu ích cho ai đó đang tìm cách tạo ra một hình ảnh 2d fractal có thể điều chỉnh được như hình này tôi cần, không có tạo tác hoặc sự biến dạng.

địa hình fractal có thể điều chỉnh

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.